{-# LANGUAGE OverloadedStrings #-}
module Network.HTTP2.Client.Helpers (
upload
, waitStream
, fromStreamResult
, StreamResult
, StreamResponse
, ping
, TimedOut
, PingReply
) where
import Data.Time.Clock (UTCTime, getCurrentTime)
import qualified Network.HTTP2 as HTTP2
import qualified Network.HPACK as HPACK
import Data.ByteString (ByteString)
import qualified Data.ByteString as ByteString
import Control.Concurrent.Lifted (threadDelay)
import Control.Concurrent.Async.Lifted (race)
import Network.HTTP2.Client
import Network.HTTP2.Client.Exceptions
data TimedOut = TimedOut
deriving Show
type PingReply = (UTCTime, UTCTime, Either TimedOut (HTTP2.FrameHeader, HTTP2.FramePayload))
ping :: Http2Client
-> Int
-> ByteString
-> ClientIO PingReply
ping conn timeout msg = do
t0 <- lift $ getCurrentTime
waitPing <- _ping conn msg
pingReply <- race (threadDelay timeout >> return TimedOut) waitPing
t1 <- lift $ getCurrentTime
return $ (t0, t1, pingReply)
type StreamResult = (Either HTTP2.ErrorCode HPACK.HeaderList, [Either HTTP2.ErrorCode ByteString], Maybe HPACK.HeaderList)
type StreamResponse = (HPACK.HeaderList, ByteString, Maybe HPACK.HeaderList)
upload :: ByteString
-> (HTTP2.FrameFlags -> HTTP2.FrameFlags)
-> Http2Client
-> OutgoingFlowControl
-> Http2Stream
-> OutgoingFlowControl
-> ClientIO ()
upload "" flagmod conn _ stream _ = do
sendData conn stream flagmod ""
upload dat flagmod conn connectionFlowControl stream streamFlowControl = do
let wanted = ByteString.length dat
gotStream <- _withdrawCredit streamFlowControl wanted
got <- _withdrawCredit connectionFlowControl gotStream
lift $ _receiveCredit streamFlowControl (gotStream - got)
let uploadChunks flagMod =
sendData conn stream flagMod (ByteString.take got dat)
if got == wanted
then
uploadChunks flagmod
else do
uploadChunks id
upload (ByteString.drop got dat) flagmod conn connectionFlowControl stream streamFlowControl
waitStream :: Http2Stream
-> IncomingFlowControl
-> PushPromiseHandler
-> ClientIO StreamResult
waitStream stream streamFlowControl ppHandler = do
ev <- _waitEvent stream
case ev of
StreamHeadersEvent fH hdrs
| HTTP2.testEndStream (HTTP2.flags fH) -> do
return (Right hdrs, [], Nothing)
| otherwise -> do
(dfrms,trls) <- waitDataFrames []
return (Right hdrs, reverse dfrms, trls)
StreamPushPromiseEvent _ ppSid ppHdrs -> do
_handlePushPromise stream ppSid ppHdrs ppHandler
waitStream stream streamFlowControl ppHandler
_ ->
error $ "expecting StreamHeadersEvent but got " ++ show ev
where
waitDataFrames xs = do
ev <- _waitEvent stream
case ev of
StreamDataEvent fh x
| HTTP2.testEndStream (HTTP2.flags fh) ->
return ((Right x):xs, Nothing)
| otherwise -> do
_ <- lift $ _consumeCredit streamFlowControl (HTTP2.payloadLength fh)
lift $ _addCredit streamFlowControl (HTTP2.payloadLength fh)
_ <- _updateWindow $ streamFlowControl
waitDataFrames ((Right x):xs)
StreamPushPromiseEvent _ ppSid ppHdrs -> do
_handlePushPromise stream ppSid ppHdrs ppHandler
waitDataFrames xs
StreamHeadersEvent _ hdrs ->
return (xs, Just hdrs)
_ ->
error $ "expecting StreamDataEvent but got " ++ show ev
fromStreamResult :: StreamResult -> Either HTTP2.ErrorCode StreamResponse
fromStreamResult (headersE, chunksE, trls) = do
hdrs <- headersE
chunks <- sequence chunksE
return (hdrs, mconcat chunks, trls)