on master help sign in
Application uses bracket pattern
commit63ad533299a0a5bad01a36171d98511fdf8d5821 authorMichael Snoyman <michael@snoyman.com> dateWed 23 Apr 2014, 18:17 (13 years ago) parent54319e9
6 files changed+58−64
wai/Network/Wai.hs+1−1 warp/Network/Wai/Handler/Warp/Response.hs+20−25 warp/Network/Wai/Handler/Warp/Run.hs+4−5 warp/test/ExceptionSpec.hs+8−8 warp/test/ResponseSpec.hs+2−2 warp/test/RunSpec.hs+23−23
wai/Network/Wai.hs+1−1
@@ -204,7 +204,7 @@ sourceFilePart handle (FilePart offset count _) =
204204----------------------------------------------------------------
205205
206206-- | The WAI application.
207−type Application = Request -> IO Response
207+type Application = Request -> (forall b. (Response -> IO b) -> IO b)
208208
209209-- | Middleware is a component that sits between the server and application. It
210210-- can do such tasks as GZIP encoding or response caching. What follows is the
warp/Network/Wai/Handler/Warp/Response.hs+20−25
@@ -16,7 +16,6 @@ import Blaze.ByteString.Builder.Internal (defaultBufferSize)
1616import Blaze.ByteString.Builder.HTTP (chunkedTransferEncoding, chunkedTransferTerminator)
1717import Control.Applicative
1818import Control.Exception
19−import Control.Monad.IO.Class (liftIO)
2019import Data.Array ((!))
2120import Data.ByteString (ByteString)
2221import Data.Streaming.Blaze (newBlazeRecv, reuseBufferStrategy, allocBuffer)
@@ -156,20 +155,19 @@ checkPartRange fileSize = checkPart
156155
157156sendResponse :: Connection
158157 -> InternalInfo
159− -> (forall a. IO a -> IO a) -- ^ Restore masking state.
160158 -> Request -- ^ HTTP request.
161159 -> IndexedHeader -- ^ Indexed header of HTTP request.
162160 -> IO ByteString -- ^ source from client, for raw response
163161 -> Response -- ^ HTTP response including status code and response header.
164162 -> IO Bool -- ^ Returing True if the connection is persistent.
165−sendResponse conn ii restore req reqidxhdr src response = do
163+sendResponse conn ii req reqidxhdr src response = do
166164 hs <- addServerAndDate hs0
167165 if hasBody s req then do
168− sendRsp conn ver s hs restore rsp
166+ sendRsp conn ver s hs rsp
169167 T.tickle th
170168 return ret
171169 else do
172− sendResponseNoBody conn ver s hs restore response
170+ sendResponseNoBody conn ver s hs response
173171 T.tickle th
174172 return isPersist
175173 where
@@ -207,17 +205,16 @@ sendRsp :: Connection
207205 -> H.HttpVersion
208206 -> H.Status
209207 -> H.ResponseHeaders
210− -> (forall a. IO a -> IO a) -- ^ restore
211208 -> Rsp
212209 -> IO ()
213−sendRsp conn ver s0 hs0 restore (RspFile path mPart mRange hook) = restore $ do
210+sendRsp conn ver s0 hs0 (RspFile path mPart mRange hook) = do
214211 ex <- fileRange s0 hs path mPart mRange
215212 case ex of
216213 Left _ex ->
217214#ifdef WARP_DEBUG
218215 print _ex >>
219216#endif
220− sendRsp conn ver s2 hs2 id (RspBuilder body True)
217+ sendRsp conn ver s2 hs2 (RspBuilder body True)
221218 Right (s, hs1, beg, len) -> do
222219 lheader <- composeHeader ver s hs1
223220 connSendFile conn path beg len hook [lheader]
@@ -229,7 +226,7 @@ sendRsp conn ver s0 hs0 restore (RspFile path mPart mRange hook) = restore $ do
229226
230227----------------------------------------------------------------
231228
232−sendRsp conn ver s hs restore (RspBuilder body needsChunked) = restore $ do
229+sendRsp conn ver s hs (RspBuilder body needsChunked) = do
233230 header <- composeHeaderBuilder ver s hs needsChunked
234231 let hdrBdy
235232 | needsChunked = header <> chunkedTransferEncoding body
@@ -241,7 +238,7 @@ sendRsp conn ver s hs restore (RspBuilder body needsChunked) = restore $ do
241238
242239----------------------------------------------------------------
243240
244−sendRsp conn ver s hs restore (RspStream withBodyFlush needsChunked th) = do
241+sendRsp conn ver s hs (RspStream withBodyFlush needsChunked th) = do
245242 header <- composeHeaderBuilder ver s hs needsChunked
246243 (recv, finish) <- newBlazeRecv $ reuseBufferStrategy $ allocBuffer defaultBufferSize
247244 let addChunk builder = do
@@ -253,17 +250,16 @@ sendRsp conn ver s hs restore (RspStream withBodyFlush needsChunked th) = do
253250 loop
254251 loop
255252 addChunk header
256− restore $ do
257− withBodyFlush $ \mbuilder -> addChunk $ case mbuilder of
258− Nothing -> flush
259− Just builder -> chunkedTransferEncoding builder
260− when needsChunked $ addChunk chunkedTransferTerminator
261− mbs <- finish
262− maybe (return ()) (connSink conn th) mbs
253+ withBodyFlush $ \mbuilder -> addChunk $ case mbuilder of
254+ Nothing -> flush
255+ Just builder -> chunkedTransferEncoding builder
256+ when needsChunked $ addChunk chunkedTransferTerminator
257+ mbs <- finish
258+ maybe (return ()) (connSink conn th) mbs
263259
264260----------------------------------------------------------------
265261
266−sendRsp conn _ _ _ restore (RspRaw withApp src tickle) =
262+sendRsp conn _ _ _ (RspRaw withApp src tickle) =
267263 withApp recv send
268264 where
269265 recv = do
@@ -278,23 +274,22 @@ sendResponseNoBody :: Connection
278274 -> H.HttpVersion
279275 -> H.Status
280276 -> H.ResponseHeaders
281− -> (forall a. IO a -> IO a) -- ^ restore
282277 -> Response
283278 -> IO ()
284−sendResponseNoBody conn ver s hs restore (ResponseStream _ _ withBodyFlush) = restore $ do
279+sendResponseNoBody conn ver s hs (ResponseStream _ _ withBodyFlush) = do
285280 -- Allow the application to free resources
286281 withBodyFlush $ const $ return ()
287282
288283 composeHeader ver s hs >>= connSendAll conn
289−sendResponseNoBody conn ver s hs restore (ResponseRaw withRaw _) = restore $ do
284+sendResponseNoBody conn ver s hs (ResponseRaw withRaw _) = do
290285 -- Allow the application to free resources
291286 withRaw (return S.empty) (const $ return ())
292287
293288 composeHeader ver s hs >>= connSendAll conn
294−sendResponseNoBody conn ver s hs restore ResponseBuilder{} =
295− restore $ composeHeader ver s hs >>= connSendAll conn
296−sendResponseNoBody conn ver s hs restore ResponseFile{} =
297− restore $ composeHeader ver s hs >>= connSendAll conn
289+sendResponseNoBody conn ver s hs ResponseBuilder{} =
290+ composeHeader ver s hs >>= connSendAll conn
291+sendResponseNoBody conn ver s hs ResponseFile{} =
292+ composeHeader ver s hs >>= connSendAll conn
298293
299294----------------------------------------------------------------
300295----------------------------------------------------------------
warp/Network/Wai/Handler/Warp/Run.hs+4−5
@@ -239,8 +239,8 @@ serveConnection conn ii addr isSecure' settings app = do
239239
240240 sendErrorResponse istatus e = do
241241 status <- readIORef istatus
242− when status $ void $ mask $ \restore ->
243− sendResponse conn ii restore dummyreq defaultIndexRequestHeader (return S.empty) (errorResponse e)
242+ when status $ void $
243+ sendResponse conn ii dummyreq defaultIndexRequestHeader (return S.empty) (errorResponse e)
244244
245245 dummyreq = defaultRequest { remoteHost = addr }
246246
@@ -255,14 +255,13 @@ serveConnection conn ii addr isSecure' settings app = do
255255 -- In the event that some scarce resource was acquired during
256256 -- creating the request, we need to make sure that we don't get
257257 -- an async exception before calling the ResponseSource.
258− keepAlive <- mask $ \restore -> do
259− res <- restore $ app req
258+ keepAlive <- app req $ \res -> do
260259 T.resume th
261260 -- FIXME consider forcing evaluation of the res here to
262261 -- send more meaningful error messages to the user.
263262 -- However, it may affect performance.
264263 writeIORef istatus False
265− sendResponse conn ii restore req idxhdr (readSource fromClient) res
264+ sendResponse conn ii req idxhdr (readSource fromClient) res
266265
267266 -- We just send a Response and it takes a time to
268267 -- receive a Request again. If we immediately call recv,
warp/test/ExceptionSpec.hs+8−8
@@ -30,20 +30,20 @@ withTestServer inner = bracket
3030 $ \_ -> inner port
3131
3232testApp :: Application
33−testApp (Network.Wai.Internal.Request {pathInfo = [x]})
33+testApp (Network.Wai.Internal.Request {pathInfo = [x]}) f
3434 | x == "statusError" =
35− return $ responseLBS undefined [] "foo"
35+ f $ responseLBS undefined [] "foo"
3636 | x == "headersError" =
37− return $ responseLBS ok200 undefined "foo"
37+ f $ responseLBS ok200 undefined "foo"
3838 | x == "headerError" =
39− return $ responseLBS ok200 [undefined] "foo"
39+ f $ responseLBS ok200 [undefined] "foo"
4040 | x == "bodyError" =
41− return $ responseLBS ok200 [] undefined
41+ f $ responseLBS ok200 [] undefined
4242 | x == "ioException" = do
4343 void $ fail "ioException"
44− return $ responseLBS ok200 [] "foo"
45−testApp _ =
46− return $ responseLBS ok200 [] "foo"
44+ f $ responseLBS ok200 [] "foo"
45+testApp _ f =
46+ f $ responseLBS ok200 [] "foo"
4747
4848spec :: Spec
4949spec = describe "responds even if there is an exception" $ do
warp/test/ResponseSpec.hs+2−2
@@ -36,7 +36,7 @@ testRange range out crange = it title $ withApp defaultSettings app $ \port -> d
3636 lookup "Content-Range" hs `shouldBe` Just ("bytes " ++ crange)
3737 lookup "Content-Length" hs `shouldBe` Just (show $ length $ last bss)
3838 where
39− app _ = return $ responseFile status200 [] "attic/hex" Nothing
39+ app _ = ($ responseFile status200 [] "attic/hex" Nothing)
4040 title = show (range, out, crange)
4141 toHeader s =
4242 case break (== ':') s of
@@ -60,7 +60,7 @@ testPartial size offset count out = it title $ withApp defaultSettings app $ \po
6060 lookup "Content-Length" hs `shouldBe` Just (show $ length $ last bss)
6161 lookup "Content-Range" hs `shouldBe` Just range
6262 where
63− app _ = return $ responseFile status200 [] "attic/hex" $ Just $ FilePart offset count size
63+ app _ = ($ responseFile status200 [] "attic/hex" $ Just $ FilePart offset count size)
6464 title = show (offset, count, out)
6565 toHeader s =
6666 case break (== ':') s of
warp/test/RunSpec.hs+23−23
@@ -45,7 +45,7 @@ err :: (MonadIO m, Show a) => Counter -> a -> m ()
4545err icount msg = liftIO $ I.writeIORef icount $ Left $ show msg
4646
4747readBody :: CounterApplication
48−readBody icount req = do
48+readBody icount req f = do
4949 body <- consumeBody $ requestBody req
5050 case () of
5151 ()
@@ -56,21 +56,21 @@ readBody icount req = do
5656 | not $ requestMethod req `elem` ["GET", "POST"]
5757 -> err icount ("Invalid request method (readBody)" :: String, requestMethod req)
5858 | otherwise -> incr icount
59− return $ responseLBS status200 [] "Read the body"
59+ f $ responseLBS status200 [] "Read the body"
6060
6161ignoreBody :: CounterApplication
62−ignoreBody icount req = do
62+ignoreBody icount req f = do
6363 if (requestMethod req `elem` ["GET", "POST"])
6464 then incr icount
6565 else err icount ("Invalid request method" :: String, requestMethod req)
66− return $ responseLBS status200 [] "Ignored the body"
66+ f $ responseLBS status200 [] "Ignored the body"
6767
6868doubleConnect :: CounterApplication
69−doubleConnect icount req = do
69+doubleConnect icount req f = do
7070 _ <- consumeBody $ requestBody req
7171 _ <- consumeBody $ requestBody req
7272 incr icount
73− return $ responseLBS status200 [] "double connect"
73+ f $ responseLBS status200 [] "double connect"
7474
7575nextPort :: I.IORef Int
7676nextPort = unsafePerformIO $ I.newIORef 5000
@@ -113,7 +113,7 @@ runTest expected app chunks = do
113113 Right i -> i `shouldBe` expected
114114
115115dummyApp :: Application
116−dummyApp _ = return $ responseLBS status200 [] "foo"
116+dummyApp _ f = f $ responseLBS status200 [] "foo"
117117
118118runTerminateTest :: InvalidRequest
119119 -> ByteString
@@ -193,9 +193,9 @@ spec = do
193193 describe "special input" $ do
194194 it "multiline headers" $ do
195195 iheaders <- I.newIORef []
196− let app req = do
196+ let app req f = do
197197 liftIO $ I.writeIORef iheaders $ requestHeaders req
198− return $ responseLBS status200 [] ""
198+ f $ responseLBS status200 [] ""
199199 withApp defaultSettings app $ \port -> do
200200 handle <- connectTo "127.0.0.1" $ PortNumber $ fromIntegral port
201201 let input = S.concat
@@ -211,9 +211,9 @@ spec = do
211211 ]
212212 it "no space between colon and value" $ do
213213 iheaders <- I.newIORef []
214− let app req = do
214+ let app req f = do
215215 liftIO $ I.writeIORef iheaders $ requestHeaders req
216− return $ responseLBS status200 [] ""
216+ f $ responseLBS status200 [] ""
217217 withApp defaultSettings app $ \port -> do
218218 handle <- connectTo "127.0.0.1" $ PortNumber $ fromIntegral port
219219 let input = S.concat
@@ -231,10 +231,10 @@ spec = do
231231 describe "chunked bodies" $ do
232232 it "works" $ do
233233 ifront <- I.newIORef id
234− let app req = do
234+ let app req f = do
235235 bss <- consumeBody $ requestBody req
236236 liftIO $ I.atomicModifyIORef ifront $ \front -> (front . (S.concat bss:), ())
237− return $ responseLBS status200 [] ""
237+ f $ responseLBS status200 [] ""
238238 withApp defaultSettings app $ \port -> do
239239 handle <- connectTo "127.0.0.1" $ PortNumber $ fromIntegral port
240240 let input = S.concat
@@ -254,10 +254,10 @@ spec = do
254254 ]
255255 it "lots of chunks" $ do
256256 ifront <- I.newIORef id
257− let app req = do
257+ let app req f = do
258258 bss <- consumeBody $ requestBody req
259259 I.atomicModifyIORef ifront $ \front -> (front . (S.concat bss:), ())
260− return $ responseLBS status200 [] ""
260+ f $ responseLBS status200 [] ""
261261 withApp defaultSettings app $ \port -> do
262262 handle <- connectTo "127.0.0.1" $ PortNumber $ fromIntegral port
263263 let input = concat $ replicate 2 $
@@ -271,10 +271,10 @@ spec = do
271271 front [] `shouldBe` replicate 2 (S.concat $ replicate 50 "12345")
272272 it "in chunks" $ do
273273 ifront <- I.newIORef id
274− let app req = do
274+ let app req f = do
275275 bss <- consumeBody $ requestBody req
276276 liftIO $ I.atomicModifyIORef ifront $ \front -> (front . (S.concat bss:), ())
277− return $ responseLBS status200 [] ""
277+ f $ responseLBS status200 [] ""
278278 withApp defaultSettings app $ \port -> do
279279 handle <- connectTo "127.0.0.1" $ PortNumber $ fromIntegral port
280280 let input = S.concat
@@ -293,7 +293,7 @@ spec = do
293293 ]
294294 it "timeout in request body" $ do
295295 ifront <- I.newIORef id
296− let app req = do
296+ let app req f = do
297297 bss <- (consumeBody $ requestBody req) `onException`
298298 liftIO (I.atomicModifyIORef ifront (\front -> (front . ("consume interrupted":), ())))
299299 liftIO $ threadDelay 4000000 `E.catch` \e -> do
@@ -302,7 +302,7 @@ spec = do
302302 , ()))
303303 E.throwIO (e :: E.SomeException)
304304 liftIO $ I.atomicModifyIORef ifront $ \front -> (front . (S.concat bss:), ())
305− return $ responseLBS status200 [] ""
305+ f $ responseLBS status200 [] ""
306306 withApp (setTimeout 1 defaultSettings) app $ \port -> do
307307 let bs1 = S.replicate 2048 88
308308 bs2 = "This is short"
@@ -322,9 +322,9 @@ spec = do
322322 S.concat (front []) `shouldBe` bs
323323 describe "raw body" $ do
324324 it "works" $ do
325− let app _req = do
325+ let app _req f = do
326326 let backup = responseLBS status200 [] "Not raw"
327− return $ flip responseRaw backup $ \src sink -> do
327+ f $ flip responseRaw backup $ \src sink -> do
328328 let loop = do
329329 bs <- src
330330 unless (S.null bs) $ do
@@ -342,7 +342,7 @@ spec = do
342342 timeout 100000 (S.hGet handle 10) >>= (`shouldBe` Just "6677889900")
343343
344344 it "only one date and server header" $ do
345− let app _ = return $ responseLBS status200
345+ let app _ f = f $ responseLBS status200
346346 [ ("server", "server")
347347 , ("date", "date")
348348 ] ""
@@ -354,7 +354,7 @@ spec = do
354354 `shouldBe` ["date"]
355355
356356 it "streaming echo #249" $ do
357− let app req = return $ responseStream status200 [] $ \write -> do
357+ let app req f = f $ responseStream status200 [] $ \write -> do
358358 let loop = do
359359 bs <- requestBody req
360360 unless (S.null bs) $ do