Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package ghc-network-control for openSUSE:Factory checked in at 2024-11-12 19:20:14 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/ghc-network-control (Old) and /work/SRC/openSUSE:Factory/.ghc-network-control.new.2017 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "ghc-network-control" Tue Nov 12 19:20:14 2024 rev:2 rq:1222959 version:0.1.3 Changes: -------- --- /work/SRC/openSUSE:Factory/ghc-network-control/ghc-network-control.changes 2023-12-05 17:03:57.634793848 +0100 +++ /work/SRC/openSUSE:Factory/.ghc-network-control.new.2017/ghc-network-control.changes 2024-11-12 19:20:41.193620558 +0100 @@ -1,0 +2,24 @@ +Thu Jul 18 22:56:21 UTC 2024 - Peter Simons <psim...@suse.com> + +- Update network-control to version 0.1.3. + ## 0.1.3 + + * Simplify `maybeOpenRxWindow` and improve docs + [#7](https://github.com/kazu-yamamoto/network-control/pull/7) + + ## 0.1.2 + + * introducing a minimum size for window update + [#5](https://github.com/kazu-yamamoto/network-control/pull/5) + + ## 0.1.1 + + * Change defaultMaxData + [#4](https://github.com/kazu-yamamoto/network-control/pull/4) + + ## 0.1.0 + + * Breaking change: Renaming rxfWindow to rxfBufSize. + * Updating the document about flow control. + +------------------------------------------------------------------- Old: ---- network-control-0.0.2.tar.gz New: ---- network-control-0.1.3.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ ghc-network-control.spec ++++++ --- /var/tmp/diff_new_pack.LJih3E/_old 2024-11-12 19:20:42.977695362 +0100 +++ /var/tmp/diff_new_pack.LJih3E/_new 2024-11-12 19:20:42.989695865 +0100 @@ -1,7 +1,7 @@ # # spec file for package ghc-network-control # -# Copyright (c) 2023 SUSE LLC +# Copyright (c) 2024 SUSE LLC # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed @@ -18,8 +18,9 @@ %global pkg_name network-control %global pkgver %{pkg_name}-%{version} +%bcond_with tests Name: ghc-%{pkg_name} -Version: 0.0.2 +Version: 0.1.3 Release: 0 Summary: Library to control network protocols License: BSD-3-Clause @@ -34,6 +35,16 @@ BuildRequires: ghc-unix-time-devel BuildRequires: ghc-unix-time-prof ExcludeArch: %{ix86} +%if %{with tests} +BuildRequires: ghc-QuickCheck-devel +BuildRequires: ghc-QuickCheck-prof +BuildRequires: ghc-hspec-devel +BuildRequires: ghc-hspec-prof +BuildRequires: ghc-pretty-simple-devel +BuildRequires: ghc-pretty-simple-prof +BuildRequires: ghc-text-devel +BuildRequires: ghc-text-prof +%endif %description Common parts to control network protocols. @@ -74,6 +85,9 @@ %install %ghc_lib_install +%check +%cabal_test + %post devel %ghc_pkg_recache ++++++ network-control-0.0.2.tar.gz -> network-control-0.1.3.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/network-control-0.0.2/Changelog.md new/network-control-0.1.3/Changelog.md --- old/network-control-0.0.2/Changelog.md 2001-09-09 03:46:40.000000000 +0200 +++ new/network-control-0.1.3/Changelog.md 2001-09-09 03:46:40.000000000 +0200 @@ -1,5 +1,25 @@ # Revision history for network-control +## 0.1.3 + +* Simplify `maybeOpenRxWindow` and improve docs + [#7](https://github.com/kazu-yamamoto/network-control/pull/7) + +## 0.1.2 + +* introducing a minimum size for window update + [#5](https://github.com/kazu-yamamoto/network-control/pull/5) + +## 0.1.1 + +* Change defaultMaxData + [#4](https://github.com/kazu-yamamoto/network-control/pull/4) + +## 0.1.0 + +* Breaking change: Renaming rxfWindow to rxfBufSize. +* Updating the document about flow control. + ## 0.0.2 * Adding constants. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/network-control-0.0.2/Network/Control/Flow.hs new/network-control-0.1.3/Network/Control/Flow.hs --- old/network-control-0.0.2/Network/Control/Flow.hs 2001-09-09 03:46:40.000000000 +0200 +++ new/network-control-0.1.3/Network/Control/Flow.hs 2001-09-09 03:46:40.000000000 +0200 @@ -1,20 +1,27 @@ {-# LANGUAGE RecordWildCards #-} module Network.Control.Flow ( - -- * Constants for flow control. + -- * Flow control + + -- | This is based on the total approach of QUIC rather than + -- the difference approach of HTTP\/2 because QUIC'one is + -- considered safer. Please refer to [Using HTTP\/3 Stream Limits in HTTP\/2](https://datatracker.ietf.org/doc/draft-thomson-httpbis-h2-stream-limits/) to understand that QUIC's approaches are better though its topic is about stream concurrency. + + -- ** Constants for flow control. defaultMaxStreams, defaultMaxStreamData, defaultMaxData, - -- * Flow control for sending + -- ** Flow control for sending TxFlow (..), newTxFlow, txWindowSize, WindowSize, - -- * Flow control for receiving + -- ** Flow control for receiving RxFlow (..), newRxFlow, + rxWindowSize, FlowControlType (..), maybeOpenRxWindow, checkRxLimit, @@ -30,21 +37,41 @@ defaultMaxStreamData :: Int defaultMaxStreamData = 262144 --- | Default max data of a connection. (1M bytes) +-- | Default max data of a connection. +-- +-- By default, this is set to @defaultMaxStreams * defaultMaxStreamData@. This +-- ensures that streams that are not currently handled cannot exhaust the +-- connection window. +-- +-- If you use a smaller connection window size, you __must__ ensure that if you +-- are handling fewer concurrent streams than allowed by 'defaultMaxStreams', +-- that the unhandled streams cannot exhaust the connection window, or risk the +-- entire system deadlocking. defaultMaxData :: Int -defaultMaxData = 1048576 +defaultMaxData = defaultMaxStreamData * defaultMaxStreams -- | Window size. type WindowSize = Int -- | Flow for sending +-- +-- @ +-- --------------------------------------> +-- ^ ^ +-- txfSent txfLimit +-- +-- |-----------| The size which this node can send +-- txWindowSize +-- @ data TxFlow = TxFlow { txfSent :: Int + -- ^ The total size of sent data. , txfLimit :: Int + -- ^ The total size of data which can be sent. } - deriving (Show) + deriving (Eq, Show) --- | Creating TX flow with an initial window size. +-- | Creating TX flow with a receive buffer size. newTxFlow :: WindowSize -> TxFlow newTxFlow win = TxFlow 0 win @@ -52,19 +79,71 @@ txWindowSize :: TxFlow -> WindowSize txWindowSize TxFlow{..} = txfLimit - txfSent --- | Flow for receiving +-- | Flow for receiving. +-- +-- The goal of 'RxFlow' is to ensure that our network peer does not send us data +-- faster than we can consume it. We therefore impose a maximum number of +-- unconsumed bytes that we are willing to receive from the peer, which we refer +-- to as the buffer size: +-- +-- @ +-- rxfBufSize +-- |---------------------------| +-- --------------------------------------------> +-- ^ ^ +-- rxfConsumed rxvReceived +-- @ +-- +-- The peer does not know of course how many bytes we have consumed of the data +-- that they sent us, so they keep track of their own limit of how much data +-- they are allowed to send. We keep track of this limit also: +-- +-- @ +-- rxfBufSize +-- |---------------------------| +-- --------------------------------------------> +-- ^ ^ ^ +-- rxfConsumed rxvReceived | +-- rxfLimit +-- @ +-- +-- Each time we receive data from the peer, we check that they do not exceed the +-- limit ('checkRxLimit'). When we consume data, we periodically send the peer +-- an update (known as a _window update_) of what their new limit is +-- ('maybeOpenRxWindow'). To decrease overhead, we only this if the window +-- update is at least half the window size. data RxFlow = RxFlow - { rxfWindow :: WindowSize + { rxfBufSize :: Int + -- ^ Maxinum number of unconsumed bytes the peer can send us + -- + -- See discussion above for details. , rxfConsumed :: Int + -- ^ How much of the data that the peer has sent us have we consumed? + -- + -- This is an absolute number: the total about of bytes consumed over the + -- lifetime of the connection or stream (i.e., not relative to the window). , rxfReceived :: Int + -- ^ How much data have we received from the peer? + -- + -- Like 'rxfConsumed', this is an absolute number. , rxfLimit :: Int + -- ^ Current limit on how many bytes the peer is allowed to send us. + -- + -- Like 'rxfConsumed, this is an absolute number. } - deriving (Show) + deriving (Eq, Show) -- | Creating RX flow with an initial window size. newRxFlow :: WindowSize -> RxFlow newRxFlow win = RxFlow win 0 0 win +-- | 'rxfLimit' - 'rxfReceived'. +-- +-- This is the number of bytes the peer is still allowed to send before they +-- must wait for a window update; see 'RxFlow' for details. +rxWindowSize :: RxFlow -> WindowSize +rxWindowSize RxFlow{..} = rxfLimit - rxfReceived + -- | The representation of window size update. data FlowControlType = -- | HTTP\/2 style @@ -72,10 +151,9 @@ | -- | QUIC style FCTMaxData --- | When an application consumed received data, --- this function should be called to update 'rxfConsumed'. --- If the window size is less than the half of the initial window. --- the representation of window size update is returned. +-- | Record that we have consumed some received data +-- +-- May return a window update; see 'RxFlow' for details. maybeOpenRxWindow :: Int -- ^ The consumed size. @@ -84,24 +162,28 @@ -> (RxFlow, Maybe Int) -- ^ 'Just' if the size should be informed to the peer. maybeOpenRxWindow consumed fct flow@RxFlow{..} - | available < threshold = - let limit = consumed' + rxfWindow - flow' = + | winUpdate >= threshold = + let flow' = flow - { rxfConsumed = consumed' - , rxfLimit = limit + { rxfConsumed = rxfConsumed' + , rxfLimit = rxfLimit' } update = case fct of - FCTWindowUpdate -> limit - rxfLimit - FCTMaxData -> limit + FCTWindowUpdate -> winUpdate + FCTMaxData -> rxfLimit' in (flow', Just update) | otherwise = - let flow' = flow{rxfConsumed = consumed'} + let flow' = flow{rxfConsumed = rxfConsumed'} in (flow', Nothing) where - available = rxfLimit - rxfReceived - threshold = rxfWindow `unsafeShiftR` 1 - consumed' = rxfConsumed + consumed + rxfConsumed' = rxfConsumed + consumed + + -- Minimum window update size + threshold = rxfBufSize `unsafeShiftR` 1 + + -- The window update, /if/ we choose to send it + rxfLimit' = rxfConsumed' + rxfBufSize + winUpdate = rxfLimit' - rxfLimit -- | Checking if received data is acceptable against the -- current window. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/network-control-0.0.2/Network/Control.hs new/network-control-0.1.3/Network/Control.hs --- old/network-control-0.0.2/Network/Control.hs 2001-09-09 03:46:40.000000000 +0200 +++ new/network-control-0.1.3/Network/Control.hs 2001-09-09 03:46:40.000000000 +0200 @@ -1,4 +1,5 @@ -- | Common parts to control network protocols. +-- This library assumes that 'Int' is 64bit. module Network.Control ( module Network.Control.Flow, module Network.Control.LRUCache, diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/network-control-0.0.2/network-control.cabal new/network-control-0.1.3/network-control.cabal --- old/network-control-0.0.2/network-control.cabal 2001-09-09 03:46:40.000000000 +0200 +++ new/network-control-0.1.3/network-control.cabal 2001-09-09 03:46:40.000000000 +0200 @@ -1,6 +1,6 @@ cabal-version: 3.0 name: network-control -version: 0.0.2 +version: 0.1.3 license: BSD-3-Clause license-file: LICENSE maintainer: k...@iij.ad.jp @@ -25,3 +25,22 @@ base >=4.14 && <5, psqueues, unix-time + +test-suite spec + type: exitcode-stdio-1.0 + main-is: Spec.hs + build-tool-depends: hspec-discover:hspec-discover + hs-source-dirs: test + other-modules: + Network.Control.FlowSpec + + default-language: Haskell2010 + default-extensions: Strict StrictData + ghc-options: -Wall -threaded + build-depends: + base, + hspec >=1.3, + network-control, + QuickCheck, + pretty-simple, + text diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/network-control-0.0.2/test/Network/Control/FlowSpec.hs new/network-control-0.1.3/test/Network/Control/FlowSpec.hs --- old/network-control-0.0.2/test/Network/Control/FlowSpec.hs 1970-01-01 01:00:00.000000000 +0100 +++ new/network-control-0.1.3/test/Network/Control/FlowSpec.hs 2001-09-09 03:46:40.000000000 +0200 @@ -0,0 +1,163 @@ +{-# LANGUAGE LambdaCase #-} +{-# LANGUAGE NumericUnderscores #-} +{-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE StandaloneDeriving #-} +{-# LANGUAGE ViewPatterns #-} +{-# OPTIONS_GHC -Wno-orphans -Wno-incomplete-patterns #-} + +module Network.Control.FlowSpec where + +import Data.List +import Data.Text.Lazy (unpack) +import Network.Control +import Test.Hspec +import Test.Hspec.QuickCheck +import Test.QuickCheck +import Text.Pretty.Simple + +-- types + +data Op = Consume | Receive + deriving (Eq, Show, Bounded, Enum) + +data OpWithResult = ConsumeWithResult (Maybe Int) | ReceiveWithResult Bool + deriving (Eq, Show) + +data Step op = Step {stepOp :: op, stepArg :: Int} + deriving (Eq, Show) + +data Trace = Trace + { traceStart :: RxFlow + , traceSteps :: [(Int, Step OpWithResult, RxFlow)] + } + deriving (Eq, Show) + +-- arbitrary instances + +maxWindowSize :: Int +maxWindowSize = 200 -- (more realistic: 2_000_000) + +instance Arbitrary RxFlow where + -- Prefer to generate a simple window size + arbitrary = + newRxFlow + <$> oneof + [ elements [1, 10, 50, 100] + , chooseInt (1, maxWindowSize) + ] + +instance Arbitrary Op where + arbitrary = elements [minBound ..] + +instance Arbitrary Trace where + arbitrary = do + initialFlow <- arbitrary + len <- chooseInt (0, 500) + Trace initialFlow <$> runManySteps len 0 initialFlow + where + runManySteps :: Int -> Int -> RxFlow -> Gen [(Int, Step OpWithResult, RxFlow)] + runManySteps 0 _ _ = pure [] + runManySteps len ix oldFlow = do + (newStep, newFlow) <- runStep oldFlow <$> genStep oldFlow + ((ix, newStep, newFlow) :) <$> runManySteps (len - 1) (ix + 1) newFlow + + genStep :: RxFlow -> Gen (Step Op) + genStep oldFlow = oneof [mkConsume, mkReceive] + where + -- Negative frames are non-sensical; frames larger than the window + -- size are theoretically possible (but will trivially be rejected + -- as exceeding the window). + mkReceive = + Step Receive <$> chooseInt (0, rxfBufSize oldFlow * 2) + + -- We can only consume as much as we have received + -- (but it is in principle not a problem to consume 0 bytes) + mkConsume = + Step Consume <$> chooseInt (0, rxfReceived oldFlow - rxfConsumed oldFlow) + + runStep :: RxFlow -> Step Op -> (Step OpWithResult, RxFlow) + runStep oldFlow = \case + Step Consume arg -> + let (newFlow, limitDelta) = maybeOpenRxWindow arg FCTWindowUpdate oldFlow + in (Step (ConsumeWithResult limitDelta) arg, newFlow) + Step Receive arg -> + let (newFlow, isAcceptable) = checkRxLimit arg oldFlow + in (Step (ReceiveWithResult isAcceptable) arg, newFlow) + + shrink (Trace initialFlow steps) = + concat + [ -- Take a prefix (starting with the same initialFlow) + Trace initialFlow <$> init (inits steps) + , -- Take a suffix (starting with a later initialFlow) + map shiftInitialFlow $ tail (tails steps) + ] + where + shiftInitialFlow :: [(Int, Step OpWithResult, RxFlow)] -> Trace + shiftInitialFlow [] = Trace initialFlow [] + shiftInitialFlow ((_, _, initialFlow') : rest) = Trace initialFlow' rest + +-- invariants + +assertTrace :: Trace -> Property +assertTrace (Trace initialFlow steps) = assertStep initialFlow steps + +assertStep :: RxFlow -> [(Int, Step OpWithResult, RxFlow)] -> Property +assertStep _ [] = property True +assertStep oldFlow ((ix, step, newFlow) : steps) = + (counterexample ("step #" <> show ix) check) .&. assertStep newFlow steps + where + check :: Expectation + check = case step of + Step (ConsumeWithResult limitDelta) arg -> do + -- There is no point duplicating precisely the same logic here as in + -- 'maybeOpenRxWindow': that would result in circular reasoning. + -- Instead, we leave 'maybeOpenRxWindow' some implementation + -- freedom, and only verify that the window update makes sense: + -- + -- (a) It can't be too large: the new window after the update should + -- never exceed the specified buffer size. + -- (b) It can't be too late: if we consume /all/ received data, and + -- do not allow the peer to send any further data, then the + -- system deadlocks. + -- (c) It shouldn't be too small: very small window updates are + -- wasteful. + -- + -- Within these parameters 'maybeOpenRxWindow' can decide when to + -- send window updates and how large they should be. We also don't + -- set the bound on (c) too strict. + newFlow + `shouldBe` RxFlow + { rxfBufSize = rxfBufSize oldFlow + , rxfConsumed = rxfConsumed oldFlow + arg + , rxfReceived = rxfReceived oldFlow + , rxfLimit = case limitDelta of + Nothing -> rxfLimit oldFlow + Just upd -> rxfLimit oldFlow + upd + } + -- Condition (a) + newFlow `shouldSatisfy` \flow -> + rxfLimit flow - rxfConsumed flow <= rxfBufSize flow + -- Condition (b) + newFlow `shouldSatisfy` \flow -> + rxfLimit flow > rxfConsumed flow + -- Condition (c) + limitDelta `shouldSatisfy` \mUpd -> + case mUpd of + Nothing -> True + Just upd -> upd >= rxfBufSize newFlow `div` 8 + Step (ReceiveWithResult isAcceptable) arg -> do + newFlow + `shouldBe` if isAcceptable + then + RxFlow + { rxfBufSize = rxfBufSize newFlow + , rxfConsumed = rxfConsumed oldFlow + , rxfReceived = rxfReceived oldFlow + arg + , rxfLimit = rxfLimit oldFlow + } + else oldFlow + +spec :: Spec +spec = do + focus . prop "state transition graph checks out" $ + \trace -> counterexample (unpack $ pShowNoColor trace) (assertTrace trace) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/network-control-0.0.2/test/Spec.hs new/network-control-0.1.3/test/Spec.hs --- old/network-control-0.0.2/test/Spec.hs 1970-01-01 01:00:00.000000000 +0100 +++ new/network-control-0.1.3/test/Spec.hs 2001-09-09 03:46:40.000000000 +0200 @@ -0,0 +1 @@ +{-# OPTIONS_GHC -F -pgmF hspec-discover #-}