{-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE RecordWildCards #-} module Network.HTTP2.Arch.Window where import Data.IORef import qualified UnliftIO.Exception as E import UnliftIO.STM import Imports import Network.HTTP2.Arch.Config import Network.HTTP2.Arch.Context import Network.HTTP2.Arch.EncodeFrame import Network.HTTP2.Arch.Queue import Network.HTTP2.Arch.Stream import Network.HTTP2.Arch.Types import Network.HTTP2.Frame getStreamWindowSize :: Stream -> IO WindowSize getStreamWindowSize Stream{streamWindow} = readTVarIO streamWindow getConnectionWindowSize :: Context -> IO WindowSize getConnectionWindowSize Context{txConnectionWindow} = readTVarIO txConnectionWindow waitStreamWindowSize :: Stream -> IO () waitStreamWindowSize Stream{streamWindow} = atomically $ do w <- readTVar streamWindow checkSTM (w > 0) waitConnectionWindowSize :: Context -> STM () waitConnectionWindowSize Context{txConnectionWindow} = do w <- readTVar txConnectionWindow checkSTM (w > 0) ---------------------------------------------------------------- -- Receiving window update increaseWindowSize :: StreamId -> TVar WindowSize -> WindowSize -> IO () increaseWindowSize sid tvar n = do w <- atomically $ do w0 <- readTVar tvar let w1 = w0 + n writeTVar tvar w1 return w1 when (isWindowOverflow w) $ do let msg = fromString ("window update for stream " ++ show sid ++ " is overflow") err = if isControl sid then ConnectionErrorIsSent else StreamErrorIsSent E.throwIO $ err FlowControlError sid msg increaseStreamWindowSize :: Stream -> WindowSize -> IO () increaseStreamWindowSize Stream{streamNumber,streamWindow} n = increaseWindowSize streamNumber streamWindow n increaseConnectionWindowSize :: Context -> Int -> IO () increaseConnectionWindowSize Context{txConnectionWindow} n = increaseWindowSize 0 txConnectionWindow n decreaseWindowSize :: Context -> Stream -> WindowSize -> IO () decreaseWindowSize Context{txConnectionWindow} Stream{streamWindow} siz = do atomically $ modifyTVar' txConnectionWindow (subtract siz) atomically $ modifyTVar' streamWindow (subtract siz) ---------------------------------------------------------------- -- Sending window update informWindowUpdate :: Context -> Stream -> IORef Int -> Int -> IO () informWindowUpdate _ _ _ 0 = return () informWindowUpdate Context{controlQ,rxConnectionInc} Stream{streamNumber} streamInc len = do join $ atomicModifyIORef rxConnectionInc $ modify 0 join $ atomicModifyIORef streamInc $ modify streamNumber where modify sid w0 | w1 < thresh = (w1, return ()) | otherwise = let frame = windowUpdateFrame sid w1 cframe = CFrames Nothing [frame] action = enqueueControl controlQ cframe in (0, action) where thresh = defaultWindowSize -- fixme w1 = w0 + len ---------------------------------------------------------------- -- max: 2,147,483,647 (2^31-1) is too large. -- def: 65,535 (2^16-1) it too small. -- 1,048,575 (2^20-1) properWindowSize :: WindowSize properWindowSize = 1048575 updateMySettings :: Config -> Context -> IO [ByteString] updateMySettings Config{..} Context{myFirstSettings,myPendingAlist} = do writeIORef myFirstSettings True writeIORef myPendingAlist $ Just myInitialAlist return frames where len = confBufferSize - frameHeaderLength payloadLen = max defaultPayloadLength len myInitialAlist = -- confBufferSize is the size of the write buffer. -- But we assume that the size of the read buffer is the same size. -- So, the size is announced to via SETTINGS_MAX_FRAME_SIZE. [(SettingsMaxFrameSize,payloadLen) ,(SettingsMaxConcurrentStreams,recommendedConcurrency) -- Initial window size for streams ,(SettingsInitialWindowSize,properWindowSize)] frame1 = settingsFrame id myInitialAlist -- Initial window update for connection frame2 = windowUpdateFrame 0 (properWindowSize - defaultWindowSize) frames = [frame1,frame2] -- Peer SETTINGS_INITIAL_WINDOW_SIZE -- Adjusting initial window size for streams updatePeerSettings :: Context -> SettingsList -> IO () updatePeerSettings Context{peerSettings,streamTable} peerAlist = do oldws <- initialWindowSize <$> readIORef peerSettings modifyIORef' peerSettings $ \old -> updateSettings old peerAlist newws <- initialWindowSize <$> readIORef peerSettings let diff = newws - oldws when (diff /= 0) $ updateAllStreamWindow (+ diff) streamTable