{-# LANGUAGE OverloadedStrings #-}

-- | HTTP\/2 server library.
--
--  Example:
--
-- > {-# LANGUAGE OverloadedStrings #-}
-- > module Main (main) where
-- >
-- > import qualified Control.Exception as E
-- > import Data.ByteString.Builder (byteString)
-- > import Network.HTTP.Types (ok200)
-- > import Network.Run.TCP (runTCPServer) -- network-run
-- >
-- > import Network.HTTP2.Server
-- >
-- > main :: IO ()
-- > main = runTCPServer Nothing "80" runHTTP2Server
-- >   where
-- >     runHTTP2Server s = E.bracket (allocSimpleConfig s 4096)
-- >                                  freeSimpleConfig
-- >                                  (\config -> run defaultServerConfig config server)
-- >     server _req _aux sendResponse = sendResponse response []
-- >       where
-- >         response = responseBuilder ok200 header body
-- >         header = [("Content-Type", "text/plain")]
-- >         body = byteString "Hello, world!\n"
module Network.HTTP2.Server (
    -- * Runner
    run,

    -- * Server configuration
    ServerConfig,
    defaultServerConfig,
    numberOfWorkers,
    connectionWindowSize,
    settings,

    -- * HTTP\/2 setting
    Settings,
    defaultSettings,
    headerTableSize,
    enablePush,
    maxConcurrentStreams,
    initialWindowSize,
    maxFrameSize,
    maxHeaderListSize,

    -- * Common configuration
    Config (..),
    allocSimpleConfig,
    freeSimpleConfig,

    -- * HTTP\/2 server
    Server,

    -- * Request
    Request,

    -- ** Accessing request
    requestMethod,
    requestPath,
    requestAuthority,
    requestScheme,
    requestHeaders,
    requestBodySize,
    getRequestBodyChunk,
    getRequestTrailers,

    -- * Aux
    Aux,
    auxTimeHandle,
    auxMySockAddr,
    auxPeerSockAddr,

    -- * Response
    Response,

    -- ** Creating response
    responseNoBody,
    responseFile,
    responseStreaming,
    responseBuilder,

    -- ** Accessing response
    responseBodySize,

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

    -- * Push promise
    PushPromise,
    pushPromise,
    promiseRequestPath,
    promiseResponse,

    -- * Types
    Path,
    Authority,
    Scheme,
    FileSpec (..),
    FileOffset,
    ByteCount,

    -- * RecvN
    defaultReadN,

    -- * Position read for files
    PositionReadMaker,
    PositionRead,
    Sentinel (..),
    defaultPositionReadMaker,
) where

import Data.ByteString.Builder (Builder)
import Data.IORef (readIORef)
import qualified Network.HTTP.Types as H
import qualified Data.ByteString.UTF8 as UTF8

import Imports
import Network.HPACK
import Network.HPACK.Token
import Network.HTTP2.Frame.Types
import Network.HTTP2.H2
import Network.HTTP2.Server.Run (
    ServerConfig (..),
    defaultServerConfig,
    run,
 )
import Network.HTTP2.Server.Types

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

-- | Getting the method from a request.
requestMethod :: Request -> Maybe H.Method
requestMethod :: Request -> Maybe Method
requestMethod (Request InpObj
req) = Token -> ValueTable -> Maybe Method
getHeaderValue Token
tokenMethod ValueTable
vt
  where
    (TokenHeaderList
_, ValueTable
vt) = InpObj -> (TokenHeaderList, ValueTable)
inpObjHeaders InpObj
req

-- | Getting the path from a request.
requestPath :: Request -> Maybe Path
requestPath :: Request -> Maybe Method
requestPath (Request InpObj
req) = Token -> ValueTable -> Maybe Method
getHeaderValue Token
tokenPath ValueTable
vt
  where
    (TokenHeaderList
_, ValueTable
vt) = InpObj -> (TokenHeaderList, ValueTable)
inpObjHeaders InpObj
req

-- | Getting the authority from a request.
requestAuthority :: Request -> Maybe Authority
requestAuthority :: Request -> Maybe Authority
requestAuthority (Request InpObj
req) = Method -> Authority
UTF8.toString (Method -> Authority) -> Maybe Method -> Maybe Authority
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Token -> ValueTable -> Maybe Method
getHeaderValue Token
tokenAuthority ValueTable
vt
  where
    (TokenHeaderList
_, ValueTable
vt) = InpObj -> (TokenHeaderList, ValueTable)
inpObjHeaders InpObj
req

-- | Getting the scheme from a request.
requestScheme :: Request -> Maybe Scheme
requestScheme :: Request -> Maybe Method
requestScheme (Request InpObj
req) = Token -> ValueTable -> Maybe Method
getHeaderValue Token
tokenScheme ValueTable
vt
  where
    (TokenHeaderList
_, ValueTable
vt) = InpObj -> (TokenHeaderList, ValueTable)
inpObjHeaders InpObj
req

-- | Getting the headers from a request.
requestHeaders :: Request -> HeaderTable
requestHeaders :: Request -> (TokenHeaderList, ValueTable)
requestHeaders (Request InpObj
req) = InpObj -> (TokenHeaderList, ValueTable)
inpObjHeaders InpObj
req

-- | Getting the body size from a request.
requestBodySize :: Request -> Maybe Int
requestBodySize :: Request -> Maybe Int
requestBodySize (Request InpObj
req) = InpObj -> Maybe Int
inpObjBodySize InpObj
req

-- | Reading a chunk of the request body.
--   An empty 'ByteString' returned when finished.
getRequestBodyChunk :: Request -> IO ByteString
getRequestBodyChunk :: Request -> IO Method
getRequestBodyChunk (Request InpObj
req) = InpObj -> IO Method
inpObjBody InpObj
req

-- | Reading request trailers.
--   This function must be called after 'getRequestBodyChunk'
--   returns an empty.
getRequestTrailers :: Request -> IO (Maybe HeaderTable)
getRequestTrailers :: Request -> IO (Maybe (TokenHeaderList, ValueTable))
getRequestTrailers (Request InpObj
req) = IORef (Maybe (TokenHeaderList, ValueTable))
-> IO (Maybe (TokenHeaderList, ValueTable))
forall a. IORef a -> IO a
readIORef (InpObj -> IORef (Maybe (TokenHeaderList, ValueTable))
inpObjTrailers InpObj
req)

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

-- | Creating response without body.
responseNoBody :: H.Status -> H.ResponseHeaders -> Response
responseNoBody :: Status -> ResponseHeaders -> Response
responseNoBody Status
st ResponseHeaders
hdr = OutObj -> Response
Response (OutObj -> Response) -> OutObj -> Response
forall a b. (a -> b) -> a -> b
$ ResponseHeaders -> OutBody -> TrailersMaker -> OutObj
OutObj ResponseHeaders
hdr' OutBody
OutBodyNone TrailersMaker
defaultTrailersMaker
  where
    hdr' :: ResponseHeaders
hdr' = Status -> ResponseHeaders -> ResponseHeaders
setStatus Status
st ResponseHeaders
hdr

-- | Creating response with file.
responseFile :: H.Status -> H.ResponseHeaders -> FileSpec -> Response
responseFile :: Status -> ResponseHeaders -> FileSpec -> Response
responseFile Status
st ResponseHeaders
hdr FileSpec
fileSpec = OutObj -> Response
Response (OutObj -> Response) -> OutObj -> Response
forall a b. (a -> b) -> a -> b
$ ResponseHeaders -> OutBody -> TrailersMaker -> OutObj
OutObj ResponseHeaders
hdr' (FileSpec -> OutBody
OutBodyFile FileSpec
fileSpec) TrailersMaker
defaultTrailersMaker
  where
    hdr' :: ResponseHeaders
hdr' = Status -> ResponseHeaders -> ResponseHeaders
setStatus Status
st ResponseHeaders
hdr

-- | Creating response with builder.
responseBuilder :: H.Status -> H.ResponseHeaders -> Builder -> Response
responseBuilder :: Status -> ResponseHeaders -> Builder -> Response
responseBuilder Status
st ResponseHeaders
hdr Builder
builder = OutObj -> Response
Response (OutObj -> Response) -> OutObj -> Response
forall a b. (a -> b) -> a -> b
$ ResponseHeaders -> OutBody -> TrailersMaker -> OutObj
OutObj ResponseHeaders
hdr' (Builder -> OutBody
OutBodyBuilder Builder
builder) TrailersMaker
defaultTrailersMaker
  where
    hdr' :: ResponseHeaders
hdr' = Status -> ResponseHeaders -> ResponseHeaders
setStatus Status
st ResponseHeaders
hdr

-- | Creating response with streaming.
responseStreaming
    :: H.Status
    -> H.ResponseHeaders
    -> ((Builder -> IO ()) -> IO () -> IO ())
    -> Response
responseStreaming :: Status
-> ResponseHeaders
-> ((Builder -> IO ()) -> IO () -> IO ())
-> Response
responseStreaming Status
st ResponseHeaders
hdr (Builder -> IO ()) -> IO () -> IO ()
strmbdy = OutObj -> Response
Response (OutObj -> Response) -> OutObj -> Response
forall a b. (a -> b) -> a -> b
$ ResponseHeaders -> OutBody -> TrailersMaker -> OutObj
OutObj ResponseHeaders
hdr' (((Builder -> IO ()) -> IO () -> IO ()) -> OutBody
OutBodyStreaming (Builder -> IO ()) -> IO () -> IO ()
strmbdy) TrailersMaker
defaultTrailersMaker
  where
    hdr' :: ResponseHeaders
hdr' = Status -> ResponseHeaders -> ResponseHeaders
setStatus Status
st ResponseHeaders
hdr

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

-- | Getter for response body size. This value is available for file body.
responseBodySize :: Response -> Maybe Int
responseBodySize :: Response -> Maybe Int
responseBodySize (Response (OutObj ResponseHeaders
_ (OutBodyFile (FileSpec Authority
_ ByteCount
_ ByteCount
len)) TrailersMaker
_)) = Int -> Maybe Int
forall a. a -> Maybe a
Just (ByteCount -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral ByteCount
len)
responseBodySize Response
_ = Maybe Int
forall a. Maybe a
Nothing

-- | Setting 'TrailersMaker' to 'Response'.
setResponseTrailersMaker :: Response -> TrailersMaker -> Response
setResponseTrailersMaker :: Response -> TrailersMaker -> Response
setResponseTrailersMaker (Response OutObj
rsp) TrailersMaker
tm = OutObj -> Response
Response OutObj
rsp{outObjTrailers = tm}

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

-- | Creating push promise.
--   The third argument is traditional, not used.
pushPromise :: ByteString -> Response -> Weight -> PushPromise
pushPromise :: Method -> Response -> Int -> PushPromise
pushPromise Method
path Response
rsp Int
_ = Method -> Response -> PushPromise
PushPromise Method
path Response
rsp