    {-# LANGUAGE RankNTypes #-}

    module Network.HTTP.Semantics.Types (
        -- * Request/response as input
        InpObj (..),
        InpBody,

        -- * Request/response as output
        OutObj (..),
        OutBody (..),
        OutBodyIface (..),

        -- * Trailers maker
        TrailersMaker,
        defaultTrailersMaker,
        NextTrailersMaker (..),

        -- * File spec
        FileOffset,
        ByteCount,
        FileSpec (..),

        -- * Types
        Scheme,
        Authority,
        Path,
    ) where

    import Control.Exception (SomeException)
    import Data.ByteString.Builder (Builder)
    import Data.IORef
    import Data.Int (Int64)
    import Network.ByteOrder
    import qualified Network.HTTP.Types as H

    import Network.HTTP.Semantics.Header
    import Network.HTTP.Semantics.Trailer

    ----------------------------------------------------------------

    -- | "http" or "https".
    type Scheme = ByteString

    -- | Authority.
    type Authority = String

    -- | Path.
    type Path = ByteString

    ----------------------------------------------------------------

    type InpBody = IO (ByteString, Bool)

    data OutBody
        = OutBodyNone
        | -- | Streaming body takes a write action and a flush action.
          OutBodyStreaming ((Builder -> IO ()) -> IO () -> IO ())
        | -- | Generalization of 'OutBodyStreaming'.
          OutBodyStreamingIface (OutBodyIface -> IO ())
        | OutBodyBuilder Builder
        | OutBodyFile FileSpec

    data OutBodyIface = OutBodyIface
        { OutBodyIface -> forall x. IO x -> IO x
    outBodyUnmask :: (forall x. IO x -> IO x)
        -- ^ Unmask exceptions in the thread spawned for the request body
        --
        -- This is used in the client: we spawn the new thread for the request body
        -- with exceptions masked, and provide the body of 'OutBodyStreamingIface'
        -- with a callback to unmask them again (typically after installing an
        -- exception handler).
        --
        -- Unmasking in the server is a no-op, as here the scope of the thread that
        -- is spawned for the server is the entire handler, not just the response
        -- streaming body.
        , OutBodyIface -> Builder -> IO ()
    outBodyPush :: Builder -> IO ()
        -- ^ Push a new chunk
        --
        -- In @http2@, there is no direct correspondence between chunks and the
        -- resulting @DATA@ frames sent: the chunks are collected (written to an
        -- internal write buffer) until we can fill a frame.
        --
        -- See also 'outBodyFlush'.
        , OutBodyIface -> Builder -> IO ()
    outBodyPushFinal :: Builder -> IO ()
        -- ^ Push the final chunk
        --
        -- Using this function instead of 'outBodyPush' can be used to guarantee
        -- that the final HTTP2 DATA frame is marked end-of-stream; with
        -- 'outBodyPush' it may happen that an additional empty DATA frame is used
        -- for this purpose. Additionally, after calling this function,
        -- 'outBodyCancel' will be a no-op.
        , OutBodyIface -> Maybe SomeException -> IO ()
    outBodyCancel :: Maybe SomeException -> IO ()
        -- ^ Cancel the stream
        --
        -- Sends a @RST_STREAM@ to the peer. If cancelling as the result of an
        -- exception, a 'Just' should be provided which specifies the exception
        -- which will be stored locally as the reason for cancelling the stream; in
        -- this case, the error code sent with the @RST_STREAM@ will be
        -- @INTERNAL_ERROR@ (see
        -- <https://datatracker.ietf.org/doc/html/rfc7540#section-7>). If 'Nothing'
        -- is given, the error code will be @CANCEL@.
        --
        -- If there is a partially constructed @DATA@ frame at the time of
        -- cancellation, this frame is discarded. If this is undesirable, you should
        -- call 'outBodyFlush' prior to cancelling.
        , OutBodyIface -> IO ()
    outBodyFlush :: IO ()
        -- ^ Flush
        --
        -- This can be used to emit a DATA frame with the data collected so far
        -- (using 'outBodyPush'), even if that DATA frame has not yet reached the
        -- maximum frame size. Calling 'outBodyFlush' unnecessarily can therefore
        -- result in excessive overhead from frame headers.
        --
        -- If no data is available to send, this is a no-op.
        }

    -- | Input object
    data InpObj = InpObj
        { InpObj -> TokenHeaderTable
    inpObjHeaders :: TokenHeaderTable
        -- ^ Accessor for headers.
        , InpObj -> Maybe Int
    inpObjBodySize :: Maybe Int
        -- ^ Accessor for body length specified in content-length:.
        , InpObj -> InpBody
    inpObjBody :: InpBody
        -- ^ Accessor for body.
        , InpObj -> IORef (Maybe TokenHeaderTable)
    inpObjTrailers :: IORef (Maybe TokenHeaderTable)
        -- ^ Accessor for trailers.
        }

    instance Show InpObj where
        show :: InpObj -> String
    show (InpObj (TokenHeaderList
    thl, ValueTable
    _) Maybe Int
    _ InpBody
    _body IORef (Maybe TokenHeaderTable)
    _tref) = TokenHeaderList -> String
    forall a. Show a => a -> String
    show TokenHeaderList
    thl

    -- | Output object
    data OutObj = OutObj
        { OutObj -> [Header]
    outObjHeaders :: [H.Header]
        -- ^ Accessor for header.
        , OutObj -> OutBody
    outObjBody :: OutBody
        -- ^ Accessor for outObj body.
        , OutObj -> TrailersMaker
    outObjTrailers :: TrailersMaker
        -- ^ Accessor for trailers maker.
        }

    instance Show OutObj where
        show :: OutObj -> String
    show (OutObj [Header]
    hdr OutBody
    _ TrailersMaker
    _) = [Header] -> String
    forall a. Show a => a -> String
    show [Header]
    hdr

    ----------------------------------------------------------------

    -- | Offset for file.
    type FileOffset = Int64

    -- | How many bytes to read
    type ByteCount = Int64

    -- | File specification.
    data FileSpec = FileSpec FilePath FileOffset ByteCount deriving (FileSpec -> FileSpec -> Bool
    (FileSpec -> FileSpec -> Bool)
    -> (FileSpec -> FileSpec -> Bool) -> Eq FileSpec
    forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
    $c== :: FileSpec -> FileSpec -> Bool
    == :: FileSpec -> FileSpec -> Bool
    $c/= :: FileSpec -> FileSpec -> Bool
    /= :: FileSpec -> FileSpec -> Bool
    Eq, Int -> FileSpec -> ShowS
    [FileSpec] -> ShowS
    FileSpec -> String
    (Int -> FileSpec -> ShowS)
    -> (FileSpec -> String) -> ([FileSpec] -> ShowS) -> Show FileSpec
    forall a.
    (Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
    $cshowsPrec :: Int -> FileSpec -> ShowS
    showsPrec :: Int -> FileSpec -> ShowS
    $cshow :: FileSpec -> String
    show :: FileSpec -> String
    $cshowList :: [FileSpec] -> ShowS
    showList :: [FileSpec] -> ShowS
    Show)