{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE RecordWildCards #-}

-- | A client library for HTTP/0.9.
module Network.HQ.Client (
    -- * Runner
    run,

    -- * Runner arguments
    H3.ClientConfig (..),
    H3.Config (..),
    H3.allocSimpleConfig,
    H3.freeSimpleConfig,
    Scheme,
    Authority,

    -- * HQ client
    Client,

    -- * Request
    Request,

    -- * Creating request
    requestNoBody,

    -- * Response
    Response,

    -- ** Accessing response
    getResponseBodyChunk,
) where

import qualified Control.Exception as E
import qualified Data.ByteString as BS
import Data.IORef
import Network.HPACK
import Network.HTTP.Semantics
import Network.HTTP.Semantics.Client
import Network.HTTP.Semantics.Client.Internal
import Network.QUIC (Connection)
import qualified Network.QUIC as QUIC
import Network.QUIC.Internal (possibleMyStreams)

import Imports
import qualified Network.HTTP3.Client as H3
import Network.HTTP3.Recv (newSource, readSource')

-- | Running an HQ client.
run :: Connection -> H3.ClientConfig -> H3.Config -> Client a -> IO a
run :: forall a. Connection -> ClientConfig -> Config -> Client a -> IO a
run Connection
conn ClientConfig
_ Config
_ Client a
client = Client a
client (Connection -> Request -> (Response -> IO r) -> IO r
forall a. Connection -> Request -> (Response -> IO a) -> IO a
sendRequest Connection
conn) Aux
aux
  where
    aux :: Aux
aux =
        Aux
            { auxPossibleClientStreams :: IO Int
auxPossibleClientStreams = Connection -> IO Int
possibleMyStreams Connection
conn
            }

sendRequest :: Connection -> Request -> (Response -> IO a) -> IO a
sendRequest :: forall a. Connection -> Request -> (Response -> IO a) -> IO a
sendRequest Connection
conn (Request OutObj
outobj) Response -> IO a
processResponse = IO Stream -> (Stream -> IO ()) -> (Stream -> IO a) -> IO a
forall a b c. IO a -> (a -> IO b) -> (a -> IO c) -> IO c
E.bracket IO Stream
open Stream -> IO ()
close ((Stream -> IO a) -> IO a) -> (Stream -> IO a) -> IO a
forall a b. (a -> b) -> a -> b
$ \Stream
strm -> do
    let hdr :: [Header]
hdr = OutObj -> [Header]
outObjHeaders OutObj
outobj
        path :: ByteString
path = Maybe ByteString -> ByteString
forall a. HasCallStack => Maybe a -> a
fromJust (Maybe ByteString -> ByteString) -> Maybe ByteString -> ByteString
forall a b. (a -> b) -> a -> b
$ HeaderName -> [Header] -> Maybe ByteString
forall a b. Eq a => a -> [(a, b)] -> Maybe b
lookup HeaderName
":path" [Header]
hdr
        requestLine :: ByteString
requestLine = [ByteString] -> ByteString
BS.concat [ByteString
"GET ", ByteString
path, ByteString
"\r\n"]
    Stream -> ByteString -> IO ()
QUIC.sendStream Stream
strm ByteString
requestLine
    Stream -> IO ()
QUIC.shutdownStream Stream
strm
    Source
src <- Stream -> IO Source
newSource Stream
strm
    IORef (Maybe TokenHeaderTable)
refH <- Maybe TokenHeaderTable -> IO (IORef (Maybe TokenHeaderTable))
forall a. a -> IO (IORef a)
newIORef Maybe TokenHeaderTable
forall a. Maybe a
Nothing
    TokenHeaderTable
vt <- [Header] -> IO TokenHeaderTable
toTokenHeaderTable []
    let readB :: IO (ByteString, Bool)
readB = Source -> IO (ByteString, Bool)
readSource' Source
src
        rsp :: Response
rsp = InpObj -> Response
Response (InpObj -> Response) -> InpObj -> Response
forall a b. (a -> b) -> a -> b
$ TokenHeaderTable
-> Maybe Int
-> IO (ByteString, Bool)
-> IORef (Maybe TokenHeaderTable)
-> InpObj
InpObj TokenHeaderTable
vt Maybe Int
forall a. Maybe a
Nothing IO (ByteString, Bool)
readB IORef (Maybe TokenHeaderTable)
refH
    Response -> IO a
processResponse Response
rsp
  where
    open :: IO Stream
open = Connection -> IO Stream
QUIC.stream Connection
conn
    close :: Stream -> IO ()
close = Stream -> IO ()
QUIC.closeStream