packages feed

quic-0.3.17: Network/QUIC/Types/Queue.hs

module Network.QUIC.Types.Queue where

import Control.Concurrent.STM
import Control.Monad (when)

import Network.QUIC.Types.Packet

-- | How many packets a connection may have waiting to be decrypted.
--
-- A server picks the connection a packet belongs to by the connection ID,
-- which is in the clear, and hands the packet over; whether the packet is
-- worth anything is settled later, in the receiver, by decrypting it.  So
-- anyone who knows a connection ID can make a server queue packets, and
-- they cost the sender less to send than the AEAD that rejects them costs
-- the receiver to refuse.  The queue grows, and before this it grew without
-- a limit: one connection's worth of junk could take all of a server's
-- memory.
--
-- This much is several milliseconds of a receiver's work, far more than the
-- scheduling jitter the queue is there to absorb, and each packet holds the
-- datagram it was cut from, so the queue is worth a few megabytes to a
-- sender rather than everything.
recvQLimit :: Int
recvQLimit = 4096

data RecvQ = RecvQ (TQueue ReceivedPacket) (TVar Int)

newRecvQ :: IO RecvQ
newRecvQ = RecvQ <$> newTQueueIO <*> newTVarIO 0

readRecvQ :: RecvQ -> IO ReceivedPacket
readRecvQ (RecvQ q qlen) = atomically $ do
    x <- readTQueue q
    modifyTVar' qlen (subtract 1)
    return x

-- | Queue a packet, or drop it if 'recvQLimit' are already waiting.
--
-- What is dropped is the packet that has just arrived rather than one
-- already queued, so that what the connection has taken on is kept and only
-- what is past the limit is lost.  A lost packet is something QUIC is built
-- to recover from.
--
-- Nothing is logged.  The only sender that can reach the limit at any rate
-- is one sending faster than the receiver can refuse what it sends, and a
-- line of log for each of those packets is the same attack by another road.
writeRecvQ :: RecvQ -> ReceivedPacket -> IO ()
writeRecvQ (RecvQ q qlen) x = atomically $ do
    n <- readTVar qlen
    when (n < recvQLimit) $ do
        writeTQueue q x
        writeTVar qlen $! n + 1

-- | Put back a packet this connection had already taken.
--
-- Not subject to the limit: these come from the packets put off for want of
-- the keys to read them, which the handshake bounds, and dropping one would
-- lose a packet the connection has already accounted for.
prependRecvQ :: RecvQ -> ReceivedPacket -> STM ()
prependRecvQ (RecvQ q qlen) x = do
    unGetTQueue q x
    modifyTVar' qlen (+ 1)