Files
simplexmq/src/Simplex/Messaging/Transport/HTTP2.hs
T
Evgeny c6e3a4d80f add missing exports (#1722)
* add missing exports

* fix dependency
2026-03-04 07:31:46 +00:00

100 lines
2.9 KiB
Haskell

{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE OverloadedStrings #-}
module Simplex.Messaging.Transport.HTTP2
( HTTP2Body (..),
HTTP2BodyChunk (..),
defaultHTTP2BufferSize,
withHTTP2,
http2TLSParams,
getHTTP2Body,
httpALPN,
httpALPN11,
) where
import qualified Control.Exception as E
import Data.ByteString.Char8 (ByteString)
import qualified Data.ByteString.Char8 as B
import Data.Default (def)
import Data.Maybe (fromMaybe)
import Foreign (mallocBytes)
import Network.HPACK (BufferSize)
import Network.HTTP2.Client (Config (..), defaultPositionReadMaker, freeSimpleConfig)
import qualified Network.HTTP2.Client as HC
import qualified Network.HTTP2.Server as HS
import Network.Socket (SockAddr (..))
import qualified Network.TLS as T
import qualified Network.TLS.Extra as TE
import Simplex.Messaging.Transport (ALPN, TLS, Transport (cGet, cPut))
import Simplex.Messaging.Transport.Buffer
import qualified System.TimeManager as TI
defaultHTTP2BufferSize :: BufferSize
defaultHTTP2BufferSize = 32768
withHTTP2 :: BufferSize -> (Config -> IO a) -> IO () -> TLS p -> IO a
withHTTP2 sz run fin c = E.bracket (allocHTTP2Config c sz) (\cfg -> freeSimpleConfig cfg `E.finally` fin) run
allocHTTP2Config :: TLS p -> BufferSize -> IO Config
allocHTTP2Config c sz = do
buf <- mallocBytes sz
tm <- TI.initialize $ 30 * 1000000
pure
Config
{ confWriteBuffer = buf,
confBufferSize = sz,
confSendAll = cPut c,
confReadN = cGet c,
confPositionReadMaker = defaultPositionReadMaker,
confTimeoutManager = tm,
confMySockAddr = SockAddrInet 0 0,
confPeerSockAddr = SockAddrInet 0 0
}
http2TLSParams :: T.Supported
http2TLSParams =
def
{ T.supportedVersions = [T.TLS13, T.TLS12],
T.supportedCiphers = TE.ciphersuite_strong_det,
T.supportedSecureRenegotiation = False
}
data HTTP2Body = HTTP2Body
{ bodyHead :: ByteString,
bodySize :: Int,
bodyPart :: Maybe (Int -> IO ByteString),
bodyBuffer :: TBuffer
}
class HTTP2BodyChunk a where
getBodyChunk :: a -> IO ByteString
getBodySize :: a -> Maybe Int
instance HTTP2BodyChunk HC.Response where
getBodyChunk = HC.getResponseBodyChunk
{-# INLINE getBodyChunk #-}
getBodySize = HC.responseBodySize
{-# INLINE getBodySize #-}
instance HTTP2BodyChunk HS.Request where
getBodyChunk = HS.getRequestBodyChunk
{-# INLINE getBodyChunk #-}
getBodySize = HS.requestBodySize
{-# INLINE getBodySize #-}
getHTTP2Body :: HTTP2BodyChunk a => a -> Int -> IO HTTP2Body
getHTTP2Body r n = do
bodyBuffer <- newTBuffer
let getPart n' = getBuffered bodyBuffer n' Nothing $ getBodyChunk r
bodyHead <- getPart n
let bodySize = fromMaybe 0 $ getBodySize r
-- TODO check bodySize once it is set
bodyPart = if B.length bodyHead == n then Just getPart else Nothing
pure HTTP2Body {bodyHead, bodySize, bodyPart, bodyBuffer}
httpALPN :: [ALPN]
httpALPN = ["h2", "http/1.1"]
httpALPN11 :: ALPN
httpALPN11 = "http/1.1"