http2-4.1.3: Network/HTTP2/Arch/Window.hs
{-# 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