packages feed

http3-0.1.10: test/HTTP3/ClientSpec.hs

{-# LANGUAGE OverloadedStrings #-}

module HTTP3.ClientSpec where

import Control.Concurrent
import qualified Control.Exception as E
import Control.Monad
import Network.HTTP.Types
import qualified Network.HTTP3.Client as C
import Network.HTTP3.Internal (
    ApplicationProtocolError (..),
    StreamResetIsReceived (..),
 )
import qualified Network.QUIC as QUIC
import qualified Network.QUIC.Client as QUICC
import qualified Network.QUIC.Server as QUICS
import System.Timeout (timeout)
import Test.Hspec

import HTTP3.Config
import HTTP3.Server (teardown)

spec :: Spec
spec =
    describe "H3 client" $
        it "tells the application of a request the server rejected" $
            E.bracket setupRejecting teardown $ \_ ->
                timeout 3000000 rejectedRequest
                    `shouldReturn` Just (Left (StreamResetIsReceived H3RequestRejected))

-- | A server that resets every request stream with H3_REQUEST_REJECTED,
-- which says the request may be retried (RFC 9114, section 4.1.1).
setupRejecting :: IO ThreadId
setupRejecting = do
    sc <- makeTestServerConfig
    tid <- forkIO $ QUICS.run sc $ \conn -> forever $ do
        strm <- QUIC.acceptStream conn
        when (QUIC.isClientInitiatedBidirectional $ QUIC.streamId strm) $
            QUIC.resetStream strm H3RequestRejected
    threadDelay 500000 -- give enough time to the server
    return tid

-- | The client used to reset the stream back with H3_MESSAGE_ERROR and throw
-- a generic error, so the application could not tell it might retry.
rejectedRequest :: IO (Either StreamResetIsReceived ())
rejectedRequest = QUICC.run testClientConfig $ \conn ->
    E.bracket C.allocSimpleConfig C.freeSimpleConfig $ \conf ->
        C.run conn testH3ClientConfig conf $ \sendRequest _aux ->
            E.try $ sendRequest (C.requestNoBody methodGet "/" []) $ \_ -> return ()