{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE NamedFieldPuns #-}
module Network.HTTP2.Server.Context where
import Control.Concurrent.STM
import Data.IORef
import Imports
import Network.HPACK
import Network.HTTP2
import Network.HTTP2.Priority
import Network.HTTP2.Server.Stream
import Network.HTTP2.Server.Types
data Context = Context {
http2settings :: !(IORef Settings)
, firstSettings :: !(IORef Bool)
, streamTable :: !StreamTable
, concurrency :: !(IORef Int)
, priorityTreeSize :: !(IORef Int)
, continued :: !(IORef (Maybe StreamId))
, clientStreamId :: !(IORef StreamId)
, serverStreamId :: !(IORef StreamId)
, inputQ :: !(TQueue Input)
, outputQ :: !(PriorityTree Output)
, controlQ :: !(TQueue Control)
, encodeDynamicTable :: !DynamicTable
, decodeDynamicTable :: !DynamicTable
, connectionWindow :: !(TVar WindowSize)
}
newContext :: IO Context
newContext = Context <$> newIORef defaultSettings
<*> newIORef False
<*> newStreamTable
<*> newIORef 0
<*> newIORef 0
<*> newIORef Nothing
<*> newIORef 0
<*> newIORef 0
<*> newTQueueIO
<*> newPriorityTree
<*> newTQueueIO
<*> newDynamicTableForEncoding defaultDynamicTableSize
<*> newDynamicTableForDecoding defaultDynamicTableSize 4096
<*> newTVarIO defaultInitialWindowSize
clearContext :: Context -> IO ()
clearContext ctx = do
clearDynamicTable $ encodeDynamicTable ctx
clearDynamicTable $ decodeDynamicTable ctx
newPushStream :: Context -> WindowSize -> Precedence -> IO Stream
newPushStream Context{serverStreamId} win pre = do
sid <- atomicModifyIORef' serverStreamId inc2
Stream sid <$> newIORef Reserved
<*> newTVarIO win
<*> newIORef pre
where
inc2 x = let !x' = x + 2 in (x', x')
{-# INLINE setStreamState #-}
setStreamState :: Context -> Stream -> StreamState -> IO ()
setStreamState _ Stream{streamState} val = writeIORef streamState val
opened :: Context -> Stream -> IO ()
opened ctx@Context{concurrency} strm = do
atomicModifyIORef' concurrency (\x -> (x+1,()))
setStreamState ctx strm (Open JustOpened)
halfClosedRemote :: Context -> Stream -> IO ()
halfClosedRemote ctx stream@Stream{streamState} = do
!closingCode <- atomicModifyIORef streamState closeHalf
traverse_ (closed ctx stream) closingCode
where
closeHalf :: StreamState -> (StreamState, Maybe ClosedCode)
closeHalf x@(Closed _) = (x, Nothing)
closeHalf (HalfClosedLocal cc) = (Closed cc, Just cc)
closeHalf _ = (HalfClosedRemote, Nothing)
halfClosedLocal :: Context -> Stream -> ClosedCode -> IO ()
halfClosedLocal ctx stream@Stream{streamState} cc = do
shouldFinalize <- atomicModifyIORef streamState closeHalf
when shouldFinalize $
closed ctx stream cc
where
closeHalf :: StreamState -> (StreamState, Bool)
closeHalf x@(Closed _) = (x, False)
closeHalf HalfClosedRemote = (Closed cc, True)
closeHalf _ = (HalfClosedLocal cc, False)
closed :: Context -> Stream -> ClosedCode -> IO ()
closed ctx@Context{concurrency,streamTable} strm@Stream{streamNumber} cc = do
remove streamTable streamNumber
atomicModifyIORef' concurrency (\x -> (x-1,()))
setStreamState ctx strm (Closed cc)