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 +14/−2
- Network/HPACK/Types.hs +0/−2
- Network/HTTP2/Client.hs +0/−1
- Network/HTTP2/Client/Run.hs +14/−8
- Network/HTTP2/Frame/Decode.hs +18/−10
- Network/HTTP2/H2/Config.hs +1/−0
- Network/HTTP2/H2/Context.hs +16/−1
- Network/HTTP2/H2/EncodeFrame.hs +3/−3
- Network/HTTP2/H2/Receiver.hs +79/−34
- Network/HTTP2/H2/Sender.hs +52/−34
- Network/HTTP2/H2/Stream.hs +6/−9
- Network/HTTP2/H2/Types.hs +1/−0
- Network/HTTP2/Server/Run.hs +10/−6
- Network/HTTP2/Server/Worker.hs +10/−4
- http2.cabal +7/−6
- util/Client.hs +33/−0
- util/Monitor.hs +1/−1
- util/Server.hs +1/−1
- util/h2c-client.hs +24/−5
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