packages feed

quic-0.3.20: test/ResetSpec.hs

{-# LANGUAGE OverloadedStrings #-}

module ResetSpec where

import Data.Bits ((.&.))
import qualified Data.ByteString as BS
import Data.List (nub)
import Test.Hspec

import Network.QUIC.Internal

spec :: Spec
spec = describe "a stateless reset" $ do
    it "has the two bits RFC 9000 fixes at 01" $ do
        flags <- mapM (const firstByte) [1 .. 200 :: Int]
        -- Header form 0 and the QUIC Bit set.  A peer that has not asked for
        -- that bit to be greased discards anything else before it looks for a
        -- token, and we are answering a packet we have no connection for, so
        -- whether it asked is what we cannot know.
        all (\w -> w .&. 0xc0 == 0x40) flags `shouldBe` True
    it "does not always send the same bits" $ do
        flags <- mapM (const firstByte) [1 .. 200 :: Int]
        length (nub flags) `shouldSatisfy` (> 1)
    it "is read as a short header packet by a peer that greases nothing" $ do
        -- Fifty of them: with one random bit wrong, one reset gets through
        -- half the time.  True here is the peer refusing a cleared QUIC Bit,
        -- which is what a peer that has not asked for greasing does.
        bss <- mapM (const $ makeStatelessReset 1280 token) [1 .. 50 :: Int]
        pkts <- concat <$> mapM (`decodePackets` True) bss
        filter broken pkts `shouldBe` []
    it "is of the size it is made" $ do
        bs <- makeStatelessReset 21 token
        BS.length bs `shouldBe` 21
    it "is half the packet it answers, up to 1280 octets" $ do
        map statelessResetSizeFor [3000, 2000, 1280, 500, 44]
            `shouldBe` [Just 1280, Just 1000, Just 640, Just 250, Just 22]
    it "is one octet less than a packet of 43 octets or fewer" $ do
        map statelessResetSizeFor [43, 22, 21]
            `shouldBe` [Just 42, Just 21, Nothing]
    it "ends a loop of two endpoints answering each other's resets" $ do
        -- RFC 9000 Sec 10.3.3.  A reset of 1280 octets used to be answered
        -- with another of 1280 octets.
        let rounds = takeWhile (/= Nothing) $ iterate (>>= statelessResetSizeFor) (Just 1500)
        length rounds `shouldSatisfy` (< 40)
    it "ends with the token" $ do
        bs <- makeStatelessReset 1280 token
        BS.drop (BS.length bs - 16) bs `shouldBe` fromStatelessResetToken token
  where
    firstByte = BS.head <$> makeStatelessReset 1280 token
    token = StatelessResetToken "0123456789abcdef"
    broken (PacketIB BrokenPacket _) = True
    broken _ = False