http2 5.4.11 → 5.4.12
raw patch · 4 files changed
+64/−8 lines, 4 filesPVP ok
version bump matches the API change (PVP)
API changes (from Hackage documentation)
Files
- ChangeLog.md +5/−0
- Network/HTTP2/H2/Context.hs +11/−7
- http2.cabal +2/−1
- test/HTTP2/ServerSpec.hs +46/−0
ChangeLog.md view
@@ -1,5 +1,10 @@ # ChangeLog for http2 +## 5.4.12++* Close a connection's streams before waiting for its threads.+ [#232](https://github.com/kazu-yamamoto/http2/pull/232)+ ## 5.4.11 * `allocSimpleConfigWith` is `allocSimpleConfig'` with an `STM` action beside
Network/HTTP2/H2/Context.hs view
@@ -28,18 +28,22 @@ ---------------------------------------------------------------- --- | Telling a connection's threads to stop and seeing them out before the--- cleanup, which closes the streams they are still writing to.+-- | Telling a connection's threads to stop, and seeing them out before+-- returning to a caller which tears the connection down under them. --+-- The cleanup comes first: it closes the streams, which is how a thread+-- blocked on a request body is told the peer is gone. Waiting for the+-- threads before it would be waiting for what the cleanup has to say.+-- -- The wait is capped because one of the threads waited for runs the--- application's handler: an application which swallows the asynchronous--- exception would otherwise keep this from ever returning.+-- application's handler: an application which never stops would otherwise+-- keep this from ever returning. It is a 'E.finally' so that the threads+-- are seen out whatever the cleanup does. stopAfterAndWait :: T.ThreadManager -> IO a -> (Maybe E.SomeException -> IO ()) -> IO a stopAfterAndWait mgr action cleanup =- T.stopAfter mgr action $ \res -> do- void $ timeout stopTimeout $ T.waitUntilAllGone mgr- cleanup res+ T.stopAfter mgr action $ \res ->+ cleanup res `E.finally` void (timeout stopTimeout $ T.waitUntilAllGone mgr) -- | How long the threads are given to end after they have been told to. -- Unwinding takes milliseconds; this is for one that does not stop.
http2.cabal view
@@ -1,6 +1,6 @@ cabal-version: 2.0 name: http2-version: 5.4.11+version: 5.4.12 license: BSD3 license-file: LICENSE maintainer: Kazu Yamamoto <kazu@iij.ad.jp>@@ -312,6 +312,7 @@ network-byte-order, network-run >= 0.7 && <0.8, random,+ time-manager >=0.5 && <0.6, typed-process test-suite spec2
test/HTTP2/ServerSpec.hs view
@@ -36,6 +36,7 @@ import System.IO import System.IO.Unsafe import System.Random+import System.ThreadManager (KilledByThreadManager (..)) import System.Timeout (timeout) import Test.Hspec @@ -138,6 +139,16 @@ runAttack cancelInFlight timeout 1000000 (takeMVar doneVar) `shouldReturn` Just () + it "tells a handler waiting on a thread of its own that the peer is gone" $ do+ -- The handler swallows the manager's kill and goes on waiting for+ -- a thread which only the request body can tell, which is what+ -- grapesy does (#231).+ doneVar <- newEmptyMVar+ withServer (runServerWaitingHandler doneVar) $ do+ threadDelay 10000+ openStreamAndDisconnect+ timeout 2000000 (takeMVar doneVar) `shouldReturn` Just True+ it "survives a padded HEADERS whose padding covers the priority fields" $ withServer runServer $ do threadDelay 10000@@ -783,6 +794,41 @@ threadDelay 200000 _ <- sendResponse responseHello [] putMVar doneVar ()++-- | A server whose handler leaves the work to a thread of its own and goes on+-- waiting for it when the manager kills it, as grapesy's does. Nothing but+-- the request body tells that thread that the peer is gone.+runServerWaitingHandler :: MVar Bool -> ServerSettings -> IO ()+runServerWaitingHandler doneVar set =+ runTCPServerWithSettings set (Just host) port runHTTP2Server+ where+ runHTTP2Server s =+ E.bracket+ (allocSimpleConfig s 32768)+ freeSimpleConfig+ (\conf -> run defaultServerConfig conf waitingServer)+ waitingServer req _aux _sendResponse = do+ told <- newEmptyMVar+ let readAll = do+ bs <- getRequestBodyChunk req+ if B.null bs then return False else readAll+ wasTold :: Either E.SomeException Bool -> Bool+ wasTold = either (const True) id+ _ <- forkIO $ E.try readAll >>= putMVar told . wasTold+ takeMVarIgnoringKill told >>= putMVar doneVar++-- | Taking an 'MVar', ignoring the manager's kill as grapesy's handler does.+takeMVarIgnoringKill :: MVar a -> IO a+takeMVarIgnoringKill var =+ takeMVar var `E.catch` \(KilledByThreadManager _) -> takeMVarIgnoringKill var++-- | Open a stream the server waits for a body on, then drop the connection.+openStreamAndDisconnect :: IO ()+openStreamAndDisconnect = runTCPClient host port $ \s -> do+ sendAll s connectionPreface+ sendAll s $ encodeFrame (EncodeInfo defaultFlags 0 Nothing) $ SettingsFrame []+ sendAll s $ openStreamFrame 1+ threadDelay 100000 runFakeServer :: MVar ByteString -> ServerSettings -> IO () runFakeServer prefaceVar set = do