packages feed

http2 5.3.10 → 5.3.11

raw patch · 19 files changed

+290/−127 lines, 19 filesdep ~http-semanticsdep ~networkdep ~network-run

Dependency ranges changed: http-semantics, network, network-run

Files

ChangeLog.md view
@@ -1,5 +1,17 @@ # ChangeLog for http2 +## 5.3.11++* Implementing `auxSendPing` for client.+* Server and client terminates their threads in the right order.+* Using `copy` in frame decoders to avoid potential fragmentation of+  `ByteString`.+* Defining `confReadNTimeout` (default to `False`). If `confReadN`+  implements timeout by itself, set it to `True`.+* TCP closing is now treaated as `ConnectionIsClosed` instead of+  `ConnectionIsTimeout`.+* GOAWAY now contains a right last streamd ID.+ ## 5.3.10  * Introducing closure.@@ -69,7 +81,7 @@   workers collaborate with the sender after queuing a response and   finish after all response data are sent. * All threads are labeled with `labelThread`. You can see them by-  `listThreas` if necessary.+  `listThreads` if necessary.  ## 5.2.6 @@ -183,7 +195,7 @@   [#80](https://github.com/kazu-yamamoto/http2/pull/80) * Introducing `KilledByHttp2ThreadManager` instead of `ThreadKilled`.   [#79](https://github.com/kazu-yamamoto/http2/pull/79)-  [#81](https://github.com/kazu-yamamoto/http2/pull/82)+  [#81](https://github.com/kazu-yamamoto/http2/pull/81)   [#82](https://github.com/kazu-yamamoto/http2/pull/82) * Handle RST_STREAM with NO_ERROR.   [#78](https://github.com/kazu-yamamoto/http2/pull/78)
Network/HPACK/Types.hs view
@@ -1,5 +1,3 @@-{-# LANGUAGE DeriveDataTypeable #-}- module Network.HPACK.Types (     -- * Header     FieldValue,
Network/HTTP2/Client.hs view
@@ -1,4 +1,3 @@-{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE RankNTypes #-}  -- | HTTP\/2 client library.
Network/HTTP2/Client/Run.hs view
@@ -80,8 +80,13 @@         n <- oddConc <$> readTVarIO (oddStreamTable ctx)         return (x - n)     aux ctx =-        Aux+        defaultAux             { auxPossibleClientStreams = possibleClientStream ctx+            , auxSendPing =+                sendPing+                    ctx+                    False+                    "Haskell!" -- 8 bytes             }     clientCore ctx req processResponse = do         (strm, moutobj) <- makeStream ctx scheme authority req@@ -129,25 +134,26 @@             connectionWindowSize             settings             confTimeoutManager+            Nothing     exchangeSettings ctx     return ctx  runH2 :: Config -> Context -> IO a -> IO a runH2 conf ctx runClient = do-    T.stopAfter mgr (try runAll >>= closureClient conf) $ \res ->+    T.stopAfter mgr (try runAll >>= closureClient conf ctx) $ \res ->         closeAllStreams (oddStreamTable ctx) (evenStreamTable ctx) res   where     mgr = threadManager ctx     runReceiver = frameReceiver ctx conf     runSender = frameSender ctx conf-    runBackgroundThreads = do-        labelMe "H2 runBackgroundThreads"-        concurrently_ runReceiver runSender-    runAll = do-        er <- race runBackgroundThreads runClient+    runClientReceiver = do+        labelMe "H2 ClientReceiver"+        er <- race runReceiver runClient         case er of-            Left () -> undefined             Right r -> return r+            -- never reached because runReceiver throws an exception to exit.+            Left () -> throwIO ConnectionIsClosed+    runAll = snd <$> concurrently runSender runClientReceiver  makeStream     :: Context
Network/HTTP2/Frame/Decode.hs view
@@ -214,11 +214,11 @@  -- | Frame payload decoder for DATA frame. decodeDataFrame :: FramePayloadDecoder-decodeDataFrame header bs = decodeWithPadding header bs DataFrame+decodeDataFrame header _bs = decodeWithPadding header _bs DataFrame  -- | Frame payload decoder for HEADERS frame. decodeHeadersFrame :: FramePayloadDecoder-decodeHeadersFrame header bs = decodeWithPadding header bs $ \bs' ->+decodeHeadersFrame header _bs = decodeWithPadding header _bs $ \bs' ->     if hasPriority         then             let (bs0, bs1) = BS.splitAt 5 bs'@@ -234,7 +234,7 @@  -- | Frame payload decoder for RST_STREAM frame. decodeRSTStreamFrame :: FramePayloadDecoder-decodeRSTStreamFrame _ bs = Right $ RSTStreamFrame $ toErrorCode (N.word32 bs)+decodeRSTStreamFrame _ bs = Right $ RSTStreamFrame $ toErrorCode $ N.word32 bs  -- | Frame payload decoder for SETTINGS frame. decodeSettingsFrame :: FramePayloadDecoder@@ -258,19 +258,22 @@  -- | Frame payload decoder for PUSH_PROMISE frame. decodePushPromiseFrame :: FramePayloadDecoder-decodePushPromiseFrame header bs = decodeWithPadding header bs $ \bs' ->+decodePushPromiseFrame header _bs = decodeWithPadding header _bs $ \bs' ->     let (bs0, bs1) = BS.splitAt 4 bs'         sid = streamIdentifier (N.word32 bs0)      in PushPromiseFrame sid bs1  -- | Frame payload decoder for PING frame. decodePingFrame :: FramePayloadDecoder-decodePingFrame _ bs = Right $ PingFrame bs+decodePingFrame _ _bs = Right $ PingFrame bs+  where+    bs = BS.copy _bs  -- | Frame payload decoder for GOAWAY frame. decodeGoAwayFrame :: FramePayloadDecoder-decodeGoAwayFrame _ bs = Right $ GoAwayFrame sid ecid bs2+decodeGoAwayFrame _ _bs = Right $ GoAwayFrame sid ecid bs2   where+    bs = BS.copy _bs     (bs0, bs1') = BS.splitAt 4 bs     (bs1, bs2) = BS.splitAt 4 bs1'     sid = streamIdentifier (N.word32 bs0)@@ -287,10 +290,14 @@  -- | Frame payload decoder for CONTINUATION frame. decodeContinuationFrame :: FramePayloadDecoder-decodeContinuationFrame _ bs = Right $ ContinuationFrame bs+decodeContinuationFrame _ _bs = Right $ ContinuationFrame bs+  where+    bs = BS.copy _bs  decodeUnknownFrame :: FrameType -> FramePayloadDecoder-decodeUnknownFrame typ _ bs = Right $ UnknownFrame typ bs+decodeUnknownFrame typ _ _bs = Right $ UnknownFrame typ bs+  where+    bs = BS.copy _bs  ---------------------------------------------------------------- @@ -311,14 +318,15 @@     -> Either FrameDecodeError FramePayload decodeWithPadding FrameHeader{..} bs body     | padded =-        let (w8, rest) = fromMaybe (error "decodeWithPadding") $ BS.uncons bs+        let (w8, rest) = fromMaybe (error "decodeWithPadding") $ BS.uncons bs'             padlen = intFromWord8 w8             bodylen = payloadLength - padlen - 1          in if bodylen < 0                 then Left $ FrameDecodeError ProtocolError streamId "padding is not enough"                 else Right . body $ BS.take bodylen rest-    | otherwise = Right $ body bs+    | otherwise = Right $ body bs'   where+    bs' = BS.copy bs     padded = testPadded flags  streamIdentifier :: Word32 -> StreamId
Network/HTTP2/H2/Config.hs view
@@ -32,6 +32,7 @@     confTimeoutManager <- T.initialize usec     confMySockAddr <- getSocketName s     confPeerSockAddr <- getPeerName s+    let confReadNTimeout = False     return Config{..}  -- | Deallocating the resource of the simple configuration.
Network/HTTP2/H2/Context.hs view
@@ -71,6 +71,7 @@     --   this requirement.     , myStreamId         :: TVar StreamId     , peerStreamId       :: IORef StreamId+    , peerLastStreamId   :: IORef StreamId     , outputBufferLimit  :: IORef Int     , outputQ            :: TQueue Output     -- ^ Invariant: Each stream will only ever have at most one 'Output'@@ -89,6 +90,8 @@     , mySockAddr         :: SockAddr     , peerSockAddr       :: SockAddr     , threadManager      :: T.ThreadManager+    , receiverDone       :: TVar Bool+    , workersDone        :: STM Bool     } {- FOURMOLU_ENABLE -} @@ -102,8 +105,9 @@     -> Int     -> Settings     -> T.Manager+    -> Maybe (STM Bool)     -> IO Context-newContext roleInfo Config{..} cacheSiz connRxWS mySettings timmgr = do+newContext roleInfo Config{..} cacheSiz connRxWS mySettings timmgr mdone = do     -- My: Use this even if ack has not been received yet.     myFirstSettings <- newIORef False     -- Peer: The spec defines max concurrency is infinite unless@@ -116,6 +120,7 @@     continued         <- newIORef Nothing     myStreamId        <- newTVarIO sid0     peerStreamId      <- newIORef 0+    peerLastStreamId  <- newIORef 0     outputBufferLimit <- newIORef buflim     outputQ           <- newTQueueIO     outputQStreamID   <- newTVarIO sid0@@ -133,6 +138,8 @@     let mySockAddr   = confMySockAddr     let peerSockAddr = confPeerSockAddr     threadManager   <- T.newThreadManager timmgr+    receiverDone    <- newTVarIO False+    let workersDone = fromMaybe (T.isAllGone threadManager) mdone     return Context{..}   where     role = case roleInfo of@@ -169,6 +176,14 @@  setPeerStreamID :: Context -> StreamId -> IO () setPeerStreamID ctx sid = writeIORef (peerStreamId ctx) sid++----------------------------------------------------------------++getPeerLastStreamId :: Context -> IO StreamId+getPeerLastStreamId ctx = readIORef $ peerLastStreamId ctx++modifyPeerLastStreamId :: Context -> StreamId -> IO ()+modifyPeerLastStreamId ctx sid = atomicModifyIORef' (peerLastStreamId ctx) $ \n -> if sid > n then (sid, ()) else (n, ())  ---------------------------------------------------------------- 
Network/HTTP2/H2/EncodeFrame.hs view
@@ -23,10 +23,10 @@   where     einfo = encodeInfo func 0 -pingFrame :: ByteString -> ByteString-pingFrame bs = encodeFrame einfo $ PingFrame bs+pingFrame :: Bool -> ByteString -> ByteString+pingFrame ack bs = encodeFrame einfo $ PingFrame bs   where-    einfo = encodeInfo setAck 0+    einfo = encodeInfo (if ack then setAck else id) 0  windowUpdateFrame :: StreamId -> WindowSize -> ByteString windowUpdateFrame sid winsiz = encodeFrame einfo $ WindowUpdateFrame winsiz
Network/HTTP2/H2/Receiver.hs view
@@ -8,6 +8,7 @@     frameReceiver,     closureClient,     closureServer,+    sendPing, ) where  import Control.Concurrent@@ -20,6 +21,7 @@ import Data.IORef import Network.Control import Network.HTTP.Semantics+import qualified System.ThreadManager as T  import Imports hiding (delete, insert) import Network.HTTP2.Frame@@ -44,17 +46,35 @@ ----------------------------------------------------------------  frameReceiver :: Context -> Config -> IO ()-frameReceiver ctx conf@Config{..} = do-    labelMe "H2 receiver"-    loop+frameReceiver ctx conf@Config{..} =+    (switch `E.catch` handler)+        `E.finally` atomically+            (writeTVar (receiverDone ctx) True)   where-    loop = do-        -- If 'confReadN' is timeouted, an exception is thrown+    handler ConnectionIsClosed = return ()+    handler e = E.throwIO e+    switch = do+        labelMe "H2 receiver"+        tid <- myThreadId+        if confReadNTimeout+            then+                loop1+            else+                void $+                    T.withHandle (threadManager ctx) (E.throwTo tid ConnectionIsTimeout) loop2+    loop1 = do+        hd <- confReadN frameHeaderLength -- throwing an exception on timeout+        when (BS.null hd) $ E.throwIO ConnectionIsClosed+        processFrame ctx conf $ decodeFrameHeader hd+        loop1+    loop2 th = do+        -- If 'confReadN' is timeouted, 'ConnectionIsTimeout' is thrown         -- to destroy the thread trees.         hd <- confReadN frameHeaderLength-        when (BS.null hd) $ E.throwIO ConnectionIsTimeout+        T.tickle th+        when (BS.null hd) $ E.throwIO ConnectionIsClosed         processFrame ctx conf $ decodeFrameHeader hd-        loop+        loop2 th  ---------------------------------------------------------------- @@ -291,14 +311,12 @@                         setframe = CFrames (Just peerAlist) (frames ++ [ack])                     writeIORef myFirstSettings True                     enqueueControl controlQ setframe-control FramePing FrameHeader{flags, streamId} bs Context{mySettings, controlQ, pingRate} =+control FramePing FrameHeader{flags, streamId} bs ctx@Context{mySettings, pingRate} =     unless (testAck flags) $ do         rate <- getRate pingRate         if rate > pingRateLimit mySettings             then E.throwIO $ ConnectionErrorIsSent EnhanceYourCalm streamId "too many ping"-            else do-                let frame = pingFrame bs-                enqueueControl controlQ $ CFrames Nothing [frame]+            else sendPing ctx True bs control FrameGoAway header bs _ = do     GoAwayFrame sid err msg <- guardIt $ decodeGoAwayFrame header bs     if err == NoError@@ -622,36 +640,63 @@  ---------------------------------------------------------------- -closureClient :: Config -> Either E.SomeException a -> IO a-closureClient Config{..} (Right x) = do-    let frame = goawayFrame 0 NoError ""-    confSendAll frame `E.catch` ignore+closureClient :: Config -> Context -> Either E.SomeException a -> IO a+closureClient conf ctx (Right x) = do+    frame <- goaway ctx NoError "no error"+    sendGoaway conf frame     return x-  where-    ignore (E.SomeException e)-        | isAsyncException e = E.throwIO e-        | otherwise = return ()-closureClient conf (Left se) = closureServer conf se+closureClient conf ctx (Left se) = closureServer conf ctx se -closureServer :: Config -> E.SomeException -> IO a-closureServer Config{..} se-    | isAsyncException se = E.throwIO se+closureServer :: Config -> Context -> E.SomeException -> IO a+closureServer conf ctx se+    | isAsyncException se = do+        frame <- goaway ctx NoError "maybe timeout by manager"+        sendGoaway conf frame+        E.throwIO se     | Just ConnectionIsClosed <- E.fromException se = do+        frame <- goaway ctx NoError "no error"+        sendGoaway conf frame         E.throwIO ConnectionIsClosed-    | Just e@(ConnectionErrorIsReceived{}) <- E.fromException se =+    | Just ConnectionIsTimeout <- E.fromException se = do+        frame <- goaway ctx NoError "timeout"+        sendGoaway conf frame+        E.throwIO ConnectionIsTimeout+    | Just e@(ConnectionErrorIsReceived _err _sid msg) <- E.fromException se = do+        frame <- goaway ctx NoError $ Short.fromShort msg+        sendGoaway conf frame         E.throwIO e-    | Just e@(ConnectionErrorIsSent err sid msg) <- E.fromException se = do-        let frame = goawayFrame sid err $ Short.fromShort msg-        confSendAll frame+    | Just e@(ConnectionErrorIsSent err _sid msg) <- E.fromException se = do+        frame <- goaway ctx err $ Short.fromShort msg+        sendGoaway conf frame         E.throwIO e-    | Just e@(StreamErrorIsSent err sid msg) <- E.fromException se = do-        let frame = resetFrame err sid-        let frame' = goawayFrame sid err $ Short.fromShort msg-        confSendAll $ frame <> frame'+    | Just e@(StreamErrorIsSent err _sid msg) <- E.fromException se = do+        let frame = resetFrame err _sid+        frame' <- goaway ctx err $ Short.fromShort msg+        sendGoaway conf (frame <> frame')         E.throwIO e-    | Just e@(StreamErrorIsReceived err sid) <- E.fromException se = do-        let frame = goawayFrame sid err "treat a stream error as a connection error"-        confSendAll frame+    | Just e@(StreamErrorIsReceived err _sid) <- E.fromException se = do+        frame <- goaway ctx err "treat a stream error as a connection error"+        sendGoaway conf frame         E.throwIO e     | Just (_ :: HTTP2Error) <- E.fromException se = E.throwIO se     | otherwise = E.throwIO $ BadThingHappen se++goaway :: Context -> ErrorCode -> ByteString -> IO ByteString+goaway ctx err msg = do+    sid <- getPeerLastStreamId ctx+    return $ goawayFrame sid err msg++sendGoaway :: Config -> ByteString -> IO ()+sendGoaway Config{..} frame = confSendAll frame `E.catch` ignore++ignore :: E.SomeException -> IO ()+ignore (E.SomeException e)+    | isAsyncException e = E.throwIO e+    | otherwise = return ()++----------------------------------------------------------------++sendPing :: Context -> Bool -> ByteString -> IO ()+sendPing Context{..} ack bs = enqueueControl controlQ $ CFrames Nothing [frame]+  where+    frame = pingFrame ack bs
Network/HTTP2/H2/Sender.hs view
@@ -1,3 +1,4 @@+{-# LANGUAGE LambdaCase #-} {-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE RecordWildCards #-}@@ -15,6 +16,7 @@ import Network.ByteOrder import Network.HTTP.Semantics.Client import Network.HTTP.Semantics.IO+import System.ThreadManager  import Imports import Network.HPACK (setLimitForEncoding, toTokenHeaderTable)@@ -57,6 +59,21 @@     updateAllStreamTxFlow siz strms =         forM_ strms $ \strm -> increaseStreamWindowSize strm siz +checkDone :: Context -> Int -> IO Bool+checkDone Context{..} 0 = atomically $ do+    isEmptyC <- isEmptyTQueue controlQ+    isEmptyO <- isEmptyTQueue outputQ+    if not isEmptyC || not isEmptyO+        then+            return False+        else do+            gone <- isAllGone threadManager+            unless gone retry+            done <- readTVar receiverDone+            unless done retry+            return True+checkDone _ _ = return False+ frameSender :: Context -> Config -> IO () frameSender     ctx@Context{outputQ, controlQ, encodeDynamicTable, outputBufferLimit}@@ -67,11 +84,13 @@         ----------------------------------------------------------------         loop :: Offset -> IO ()         loop off = do-            x <- atomically $ dequeue off-            case x of-                C ctl -> flushN off >> control ctl >> loop 0-                O out -> outputAndSync out off >>= flushIfNecessary >>= loop-                Flush -> flushN off >> loop 0+            done <- checkDone ctx off+            unless done $ do+                x <- atomically $ dequeue off+                case x of+                    C ctl -> flushN off >> control ctl >> loop 0+                    O out -> outputAndSync out off >>= flushIfNecessary >>= loop+                    Flush -> flushN off >> loop 0          -- Flush the connection buffer to the socket, where the first 'n' bytes of         -- the buffer are filled.@@ -196,35 +215,34 @@             let payloadOff = off0 + frameHeaderLength                 datBuf = confWriteBuffer `plusPtr` payloadOff                 datBufSiz = buflim - payloadOff-            curr datBuf (min datBufSiz lim) >>= \next ->-                case next of-                    Next datPayloadLen reqflush mnext -> do-                        NextTrailersMaker tlrmkr' <- runTrailersMaker tlrmkr datBuf datPayloadLen-                        fillDataHeader-                            strm-                            off0-                            datPayloadLen-                            mnext-                            tlrmkr'-                            out-                            reqflush-                    CancelNext mErr -> do-                        -- Stream cancelled-                        ---                        -- At this point, the headers have already been sent.-                        -- Therefore, the stream cannot be in the 'Idle' state, so we-                        -- are justified in sending @RST_STREAM@.-                        ---                        -- By the invariant on the 'outputQ', there are no other-                        -- outputs for this stream already enqueued. Therefore, we can-                        -- safely cancel it knowing that we won't try and send any-                        -- more data frames on this stream.-                        case mErr of-                            Just err ->-                                resetStream strm InternalError err-                            Nothing ->-                                resetStream strm Cancel (E.toException CancelledStream)-                        return (off0, Nothing)+            curr datBuf (min datBufSiz lim) >>= \case+                Next datPayloadLen reqflush mnext -> do+                    NextTrailersMaker tlrmkr' <- runTrailersMaker tlrmkr datBuf datPayloadLen+                    fillDataHeader+                        strm+                        off0+                        datPayloadLen+                        mnext+                        tlrmkr'+                        out+                        reqflush+                CancelNext mErr -> do+                    -- Stream cancelled+                    --+                    -- At this point, the headers have already been sent.+                    -- Therefore, the stream cannot be in the 'Idle' state, so we+                    -- are justified in sending @RST_STREAM@.+                    --+                    -- By the invariant on the 'outputQ', there are no other+                    -- outputs for this stream already enqueued. Therefore, we can+                    -- safely cancel it knowing that we won't try and send any+                    -- more data frames on this stream.+                    case mErr of+                        Just err ->+                            resetStream strm InternalError err+                        Nothing ->+                            resetStream strm Cancel (E.toException CancelledStream)+                    return (off0, Nothing)         output (Output strm (OPush ths pid) _) off0 _lim = do             -- Creating a push promise header             -- Frame id should be associated stream id from the client.
Network/HTTP2/H2/Stream.hs view
@@ -121,11 +121,7 @@     terminated <- newTVarIO Nothing     let whenNotTerminated act = do             mTerminated <- readTVar terminated-            case mTerminated of-                Just reason ->-                    throwSTM reason-                Nothing ->-                    act+            maybe act throwSTM mTerminated          terminateWith reason act = do             mTerminated <- readTVar terminated@@ -154,10 +150,11 @@                     atomically $                         whenNotTerminated $                             writeTBQueue tbq StreamingFlush-                , outBodyCancel = \mErr ->-                    atomically $-                        terminateWith StreamCancelled $-                            writeTBQueue tbq (StreamingCancelled mErr)+                , outBodyCancel =+                    atomically+                        . terminateWith StreamCancelled+                        . writeTBQueue tbq+                        . StreamingCancelled                 }         finished = atomically $ do             terminateWith StreamOutOfScope $
Network/HTTP2/H2/Types.hs view
@@ -269,6 +269,7 @@     -- ^ This is copied into 'Aux', if exist, on server.     , confPeerSockAddr :: SockAddr     -- ^ This is copied into 'Aux', if exist, on server.+    , confReadNTimeout :: Bool     }  isAsyncException :: Exception e => e -> Bool
Network/HTTP2/Server/Run.hs view
@@ -51,7 +51,7 @@     ok <- checkPreface conf     when ok $ do         let lnch = runServer conf server-        ctx <- setup sconf conf lnch+        ctx <- setup sconf conf lnch Nothing         runH2 conf ctx  ----------------------------------------------------------------@@ -63,6 +63,7 @@     , sioWriteResponse :: a -> Response -> IO ()     -- ^ 'Response' MUST be created with 'responseBuilder'.     -- Others are not supported.+    , sioDone :: IO ()     }  -- | Launching a receiver and a sender without workers.@@ -77,7 +78,8 @@     when ok $ do         inpQ <- newTQueueIO         let lnch _ strm inpObj = atomically $ writeTQueue inpQ (strm, inpObj)-        ctx <- setup sconf conf lnch+        done <- newTVarIO False+        ctx <- setup sconf conf lnch $ Just $ readTVar done         let get = do                 (strm, inpObj) <- atomically $ readTQueue inpQ                 return (strm, Request inpObj)@@ -94,6 +96,7 @@                     , sioPeerSockAddr = confPeerSockAddr                     , sioReadRequest = get                     , sioWriteResponse = putR+                    , sioDone = atomically $ writeTVar done True                     }         io <- action serverIO         concurrently_ io $ runH2 conf ctx@@ -107,8 +110,8 @@             return False         else return True -setup :: ServerConfig -> Config -> Launch -> IO Context-setup ServerConfig{..} conf@Config{..} lnch = do+setup :: ServerConfig -> Config -> Launch -> Maybe (STM Bool) -> IO Context+setup ServerConfig{..} conf@Config{..} lnch mIsDone = do     let serverInfo = newServerInfo lnch     newContext         serverInfo@@ -117,6 +120,7 @@         connectionWindowSize         settings         confTimeoutManager+        mIsDone  runH2 :: Config -> Context -> IO () runH2 conf ctx = do@@ -127,8 +131,8 @@             er <- E.try $ concurrently_ runReceiver runSender             case er of                 Right () -> return ()-                Left e -> closureServer conf e-    T.stopAfter mgr (runBackgroundThreads) $ \res ->+                Left e -> closureServer conf ctx e+    T.stopAfter mgr runBackgroundThreads $ \res ->         closeAllStreams (oddStreamTable ctx) (evenStreamTable ctx) res  -- connClose must not be called here since Run:fork calls it
Network/HTTP2/Server/Worker.hs view
@@ -24,11 +24,17 @@ runServer conf server ctx@Context{..} strm req =     T.forkManagedTimeout threadManager label $ \th -> do         let req' = pauseRequestBody th-            aux = Aux th mySockAddr peerSockAddr+            aux =+                defaultAux+                    { auxTimeHandle = th+                    , auxMySockAddr = mySockAddr+                    , auxPeerSockAddr = peerSockAddr+                    }             request = Request req'         lc <- newLoopCheck strm Nothing         server request aux $ sendResponse conf ctx lc strm request         adjustRxWindow ctx strm+        modifyPeerLastStreamId ctx $ streamNumber strm   where     label = "H2 response sender for stream " ++ show (streamNumber strm)     pauseRequestBody th = req{inpObjBody = readBody'}@@ -37,7 +43,7 @@         readBody' = do             T.pause th             bs <- readBody-            T.resume th+            T.resume th -- this is the same as 'tickle'             return bs  ----------------------------------------------------------------@@ -173,11 +179,11 @@                         { outBodyPush = \b -> do                             T.pause th                             outBodyPush iface b-                            T.resume th+                            T.resume th -- this is the same as 'tickle'                         , outBodyPushFinal = \b -> do                             T.pause th                             outBodyPushFinal iface b-                            T.resume th+                            T.resume th -- this is the same as 'tickle'                         }             strmbdy iface'     return tbq
http2.cabal view
@@ -1,6 +1,6 @@ cabal-version:      >=1.10 name:               http2-version:            5.3.10+version:            5.3.11 license:            BSD3 license-file:       LICENSE maintainer:         Kazu Yamamoto <kazu@iij.ad.jp>@@ -114,14 +114,14 @@         bytestring >=0.10,         case-insensitive >=1.2 && <1.3,         containers >=0.6,-        http-semantics >= 0.3 && <0.4,+        http-semantics >= 0.3.1 && <0.4,         http-types >=0.12 && <0.13,         iproute >= 1.7 && < 1.8,         network >=3.1,         network-byte-order >=0.1.7 && <0.2,         network-control >=0.1 && <0.2,         stm >=2.5 && <2.6,-        time-manager >=0.2 && <0.3,+        time-manager >=0.2 && <0.4,         unix-time >=0.4.11 && <0.5,         utf8-string >=1.0 && <1.1 @@ -138,7 +138,8 @@         bytestring,         http-types,         http2,-        network-run >= 0.3 && <0.5,+        network,+        network-run >= 0.5 && <0.6,         unix-time      if flag(devel)@@ -307,7 +308,7 @@         http-types,         http2,         network,-        network-run >=0.3.0,+        network-run >= 0.5 && <0.6,         random,         typed-process @@ -326,7 +327,7 @@         hspec >=1.3,         http-types,         http2,-        network-run >=0.3.0,+        network-run >= 0.5 && <0.6,         typed-process      if flag(h2spec)
util/Client.hs view
@@ -4,13 +4,16 @@  module Client where +import Control.Concurrent import Control.Concurrent.Async import qualified Control.Exception as E import Control.Monad+import Data.ByteString (ByteString) import qualified Data.ByteString.Char8 as C8 import Data.UnixTime import Foreign.C.Types import Network.HTTP.Types+import System.IO import Text.Printf  import Network.HTTP2.Client@@ -21,6 +24,7 @@     { optPerformance :: Int     , optNumOfReqs :: Int     , optMonitor :: Bool+    , optInteractive :: Bool     }     deriving (Show) @@ -82,3 +86,32 @@             / fromIntegral millisecs             / 1024             / 1024++console+    :: Options -> [ByteString] -> IO () -> Aux -> IO ()+console _opt paths cli aux = do+    putStrLn "q -- quit"+    putStrLn "g -- get"+    putStrLn "p -- ping"+    mvar <- newEmptyMVar+    loop mvar `E.catch` \(E.SomeException _) -> return ()+  where+    loop mvar = do+        hSetBuffering stdout NoBuffering+        putStr "> "+        hSetBuffering stdout LineBuffering+        l <- getLine+        case l of+            "q" -> putStrLn "bye"+            "g" -> do+                mapM_ (\p -> putStrLn $ "GET " ++ C8.unpack p) paths+                _ <- forkIO $ cli >> putMVar mvar ()+                takeMVar mvar+                loop mvar+            "p" -> do+                putStrLn "Ping"+                auxSendPing aux+                loop mvar+            _ -> do+                putStrLn "No such command"+                loop mvar
util/Monitor.hs view
@@ -16,7 +16,7 @@     showT (i, l, s) = i ++ " " ++ l ++ ": " ++ show s  threadSummary :: IO [(String, String, ThreadStatus)]-threadSummary = (sort <$> listThreads) >>= mapM summary+threadSummary = listThreads >>= mapM summary . sort   where     summary t = do         let idstr = drop 9 $ show t
util/Server.hs view
@@ -3,7 +3,7 @@ module Server where  import Control.Monad-import Crypto.Hash (Context, SHA1) -- cryptonite+import Crypto.Hash (Context, SHA1) -- crypton import qualified Crypto.Hash as CH import Data.ByteString (ByteString) import qualified Data.ByteString as B
util/h2c-client.hs view
@@ -1,4 +1,5 @@ {-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE RankNTypes #-}  module Main where @@ -8,6 +9,7 @@ import qualified Data.ByteString.Char8 as C8 import Network.HTTP2.Client import Network.Run.TCP (runTCPClient)+import Network.Socket import System.Console.GetOpt import System.Environment import System.Exit@@ -21,6 +23,7 @@         { optPerformance = 0         , optNumOfReqs = 1         , optMonitor = False+        , optInteractive = False         }  usage :: String@@ -43,6 +46,11 @@         ["monitor"]         (NoArg (\opts -> opts{optMonitor = True}))         "run thread monitor"+    , Option+        ['i']+        ["interactive"]+        (NoArg (\o -> o{optInteractive = True}))+        "enter interactive mode"     ]  showUsageAndExit :: String -> IO a@@ -69,8 +77,19 @@         h : p : ps -> return (h, p, C8.pack <$> ps)     when (optMonitor opts) $ void $ forkIO $ monitor $ threadDelay 1000000     let cliconf = defaultClientConfig{authority = host}-    runTCPClient host port $ \s ->-        E.bracket-            (allocSimpleConfig' s 4096 5000000)-            freeSimpleConfig-            (\conf -> run cliconf conf $ client opts paths)+    run' cliconf host port $ client' opts paths++run' :: ClientConfig -> HostName -> ServiceName -> Client a -> IO a+run' cliconf host port f = runTCPClient host port $ \s ->+    E.bracket+        (allocSimpleConfig' s 4096 10000000)+        freeSimpleConfig+        (\conf -> run cliconf conf f)++client' :: Options -> [Path] -> Client ()+client' opts paths sendRequest _aux+    | optInteractive opts = do+        let action = client opts paths sendRequest _aux+        console opts paths action _aux+        return ()+    | otherwise = client opts paths sendRequest _aux